diff mbox

Add extra location information - PR43486

Message ID 20120918105831.GA15097@adacore.com
State New
Headers show

Commit Message

Arnaud Charlet Sept. 18, 2012, 10:58 a.m. UTC
This is a relatively large patch which tackles the difficult issue of
generating more sloc information in front-ends (in particular C and C++
front-ends), so that "static analyzers" based on GCC can get more useful
and detailed information.

This happens to be related/similar to the need mentioned in PR43486, hence
the mention of this PR in the subject line.

In my work, as I mentioned a few times in the past, this patch is the
first foundation for another patch, which will provide source navigation
capabilities in IDEs/editors, by generating source cross reference information
based on the C and C++ front-ends (introducing a new switch -fdump-xref).
Ideally I'd like to submit both patches.

Since this issue is more general, I have split my changes and introduced a new
tentative switch called -fextra-slocs, which is the subject of this email.

In order to provide more sloc info attached to nodes (and in particular
VAR_DECLs), there are basically two possibilities:

- the one mentioned in comment 4 of PR43486:
  << Jason Merrill 2010-03-23 01:51:34 UTC

  I suppose we could wrap rvalue uses in NOP_EXPR and lvalue uses in
  VIEW_CONVERT_EXPR.
  >>

  I investigated this option, and unfortunately this does not work
  because of folding occurring all over the place in the C and
  C++ front-ends, removing these extra EXPRs very early.

  So I opted for the second approach:
- Keep another data structure which will associate extra slocs with some
  node expressions. The idea is that since you can't associate slocs with
  VAR_DECLs in an expr, instead we store extra slocs in the containing
  expression node. For instance, all binary expressions (add ADD, MINUS, ...)
  will contain two extra slocs: one for the left hand side, and one for the
  right hand side of the expression. One extra sloc for unary operators,
  etc...
  I've used a hash table which associates an array of location_t to some
  expr nodes, which could then be used by static analysis passes (as done
  by my patch to implement a -fdump-xref switch).

I have attached the various patches, split in directories:
- difs.common contains the common parts, introducing the -fextra-slocs
  switch and the generic data structure/API to store/retrieve extra slocs
  (in tree.[hc])
- difs.c contains the C front-end specific parts and add lots of source
  location information, add some extra loc parameters to various parser
  functions, and store these extra slocs when -fextra-slocs is enabled
- difs.cp does the same thing for the C++ front-end and includes testsuite
  updates which are triggered by this patch, showing some of the immediate
  benefits/side effects of generating more accurate "primary" slocs in 
  some cases (the case of Class::Method () references, where we now want to
  reference the sloc pointing to Method rather than Class)

OK on principle? Or is the whole approach not suitable/doomed?

If OK on principle, I'd appreciate a C, C++ front-end maintainers, and
a general maintainer (for the common/general part) to review my patches in
more details, and hopefully give their review/OK.

Here is the ChangeLog (which will be split into the various directories of
course), patches are in attachment.

2012-09-18  Arnaud Charlet  <charlet@adacore.com>

	* tree.h, tree.c (stabilize_reference): Copy source locations.
	(sloc_struct, extra_slocs): New.
	(node_hash, node_eq): Implement extra_slocs hash table.
	(expr_locations, set_expr_locations, set_expr_location2,
	duplicate_expr_locations, expr_location_n): New functions.
	(EXPR_LOCATIONS, SET_EXPR_LOCATION2, SET_EXPR_LOCATIONS): New macros.
	* common.opt: New switch -fextra-slocs.

c/
	* c-parser.c (c_parser_expr_list): New parameter locs, num_locs.
	Set extra locations.
	(c_parser_attributes): Adjust calls to c_parser_expr_list.
	(c_parser_statement_after_labels): Adjust calls to c_finish_return with
	extra expr location.
	(c_parser_expr_no_commas, c_parser_conditional_expression,
	c_parser_binary_expression, c_parser_cast_expression,
	c_parser_unary_expression, c_parser_postfix_expression_after_primary,
	c_parser_expr_list): Set extra locations.
	(c_parser_postfix_expression): Remove extra semicolon.
	(c_parser_objc_keywordexpr): Adjust call to c_parser_expr_list.
	* c-typeck.c (c_finish_return): New parameter loc_expr.
	* c-tree.h (c_finish_return): Add location_t parameter.
	* c-decl.c (finish_function): Update call to c_finish_return.
	(build_function_declarator): Set ret->id_loc.
c-family/
	* c-common.c (c_fully_fold_internal): Copy extra locations on new node.
objc/
	* objc-act.c (objc_synthesize_getter): Update call to c_finish_return.
cp/
	* parser.c (cp_parser_unary_expression, cp_parser_binary_expression,
	cp_parser_question_colon_clause, cp_parser_assignment_expression,
	cp_parser_enumerator_definition): Set extra locations.
	(cp_parser_parenthesized_expression_list): Ditto. New parameters
	locs, num_locs.
	(cp_parser_parse_and_diagnose_invalid_typ): Adjust call to
	cp_parser_id_location.
	(cp_parser_userdef_char_literal, cp_parser_userdef_numeric_literal,
	cp_parser_userdef_string_literal, cp_parser_perform_range_for_lookup,
	cp_parser_range_for_member_function): Adjust calls to finish_call_expr.
	(cp_parser_primary_expression, cp_parser_postfix_dot_deref_expression,
	cp_parser_decltype, cp_parser_type_parameter,
	cp_parser_template_argument, cp_parser_omp_var_list_no_open): Adjust
	calls to cp_parser_id_expression.
	(cp_parser_id_expression, cp_parser_declarator_id): New parameter
	location_t *. Set location to proper sloc for class::method
	declarations.
	(cp_parser_postfix_expression): Set extra locations.
	Update calls to cp_parser_parenthesized_expression_list,
	build_new_method_call and finish_call_expr.
	(cp_parser_postfix_open_square_expression): Add extra locations for
	array refs (e.g. a[i]).
	(cp_parser_direct_declarator): Adjust call to cp_parser_declarator_id
	and adjust sloc of declarator.
	(cp_parser_new_placement, cp_parser_new_initializer,
	cp_parser_mem_initializer, cp_parser_initializer,
	cp_parser_attribute_list, cp_parser_functional_cast): Adjust call to
	cp_parser_parenthesized_expression_list.
	* typeck.c (build_c_cast): Ensure extra sloc information is preserved
	over this function.
	* call.c (build_over_call): New parameter loc.
	(build_new_function_call, build_operator_new_call, build_op_call_1,
	build_new_op_1, convert_like_real): Update call to build_over_call.
	(build_new_method_call, build_new_method_call_1, finish_call_expr):
	New parameter loc.
	(build_special_member_call): Update call to build_new_method_call.
	(perform_implicit_conversion_flags): Ensure line number
	information is preserved over this function.
	* method.c (locate_fn_flags): Update call to build_new_method_call.
	* class.c (build_self_reference): Update decl location of internal type.
	* cp-tree.h (build_new_method_call, finish_call_expr): New parameter
	loc.
	* pt.c (tsubst_copy_and_build): Update call to build_new_method_call
	and finish_call_expr.
	* init.c (build_new_1, build_dtor_call): Ditto.
	* semantics.c (finish_omp_barrier, finish_omp_flush,
	finish_omp_taskyield, finish_omp_taskwait): Ditto.
	(finish_call_expr): Ditto. New parameter loc.
	(finish_id_expression): Add sloc on decl.
testsuite/
	* g++.dg/warn/pr26785.C, g++.old-deja/g++.brendan/crash16.C: Update
	column numbers.
	* g++.dg/tc1/dr52.C: Update baseline.

Index: cp/typeck.c
===================================================================
--- cp/typeck.c	(revision 190939)
+++ cp/typeck.c	(working copy)
@@ -6645,9 +6645,14 @@ build_const_cast (tree type, tree expr,
 /* Like cp_build_c_cast, but for the c-common bits.  */
 
 tree
-build_c_cast (location_t loc ATTRIBUTE_UNUSED, 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);
+
+  if (EXPR_P (result))
+    SET_EXPR_LOCATION2 (result, loc);
+
+  return result;
 }
 
 /* Build an expression representing an explicit C-style cast to type
Index: cp/call.c
===================================================================
--- cp/call.c	(revision 190939)
+++ cp/call.c	(working copy)
@@ -146,7 +146,8 @@ static int equal_functions (tree, tree);
 static int joust (struct z_candidate *, struct z_candidate *, bool,
 		  tsubst_flags_t);
 static int compare_ics (conversion *, conversion *);
-static tree build_over_call (struct z_candidate *, int, tsubst_flags_t);
+static tree build_over_call (struct z_candidate *, int, tsubst_flags_t,
+			     location_t);
 static tree build_java_interface_fn_ref (tree, tree);
 #define convert_like(CONV, EXPR, COMPLAIN)			\
   convert_like_real ((CONV), (EXPR), NULL_TREE, 0, 0,		\
@@ -3905,7 +3906,7 @@ build_new_function_call (tree fn, VEC(tr
          about peculiar null pointer conversion.  */
       if (TREE_CODE (fn) == TEMPLATE_ID_EXPR)
         flags |= LOOKUP_EXPLICIT_TMPL_ARGS;
-      result = build_over_call (cand, flags, complain);
+      result = build_over_call (cand, flags, complain, input_location);
     }
 
   /* Free all the conversions we allocated.  */
@@ -4024,7 +4025,7 @@ build_operator_new_call (tree fnname, VE
      *fn = cand->fn;
 
    /* Build the CALL_EXPR.  */
-   return build_over_call (cand, LOOKUP_NORMAL, complain);
+   return build_over_call (cand, LOOKUP_NORMAL, complain, input_location);
 }
 
 /* Build a new call to operator().  This may change ARGS.  */
@@ -4145,7 +4146,8 @@ build_op_call_1 (tree obj, VEC(tree,gc)
 	 DECL_NAME here.  */
       else if (TREE_CODE (cand->fn) == FUNCTION_DECL
 	       && DECL_OVERLOADED_OPERATOR_P (cand->fn) == CALL_EXPR)
-	result = build_over_call (cand, LOOKUP_NORMAL, complain);
+	result = build_over_call (cand, LOOKUP_NORMAL, complain,
+				  input_location);
       else
 	{
 	  obj = convert_like_with_context (cand->convs[0], obj, cand->fn, -1,
@@ -5171,7 +5173,8 @@ build_new_op_1 (location_t loc, enum tre
 	  if (resolve_args (arglist, complain) == NULL)
 	    result = error_mark_node;
 	  else
-	    result = build_over_call (cand, LOOKUP_NORMAL, complain);
+	    result = build_over_call (cand, LOOKUP_NORMAL, complain,
+				      input_location);
 	}
       else
 	{
@@ -5783,7 +5786,7 @@ convert_like_real (conversion *convs, tr
 	for (i = 0; i < cand->num_convs; ++i)
 	  cand->convs[i]->user_conv_p = true;
 
-	expr = build_over_call (cand, LOOKUP_NORMAL, complain);
+	expr = build_over_call (cand, LOOKUP_NORMAL, complain, input_location);
 
 	/* If this is a constructor or a function returning an aggr type,
 	   we need to build up a TARGET_EXPR.  */
@@ -6402,7 +6405,8 @@ magic_varargs_p (tree fn)
    bitmask of various LOOKUP_* flags which apply to the call itself.  */
 
 static tree
-build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
+build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain,
+		 location_t loc ATTRIBUTE_UNUSED)
 {
   tree fn = cand->fn;
   const VEC(tree,gc) *args = cand->args;
@@ -7130,7 +7134,7 @@ build_special_member_call (tree instance
   ret = build_new_method_call (instance, fns, args,
 			       TYPE_BINFO (BINFO_TYPE (binfo)),
 			       flags, /*fn=*/NULL,
-			       complain);
+			       complain, input_location);
 
   if (allocated != NULL)
     release_tree_vector (allocated);
@@ -7185,12 +7189,12 @@ name_as_c_string (tree name, tree type,
 
 /* Build a call to "INSTANCE.FN (ARGS)".  If FN_P is non-NULL, it will
    be set, upon return, to the function called.  ARGS may be NULL.
-   This may change ARGS.  */
+   This may change ARGS.  LOC is the source location of the call.  */
 
 static tree
 build_new_method_call_1 (tree instance, tree fns, VEC(tree,gc) **args,
 		         tree conversion_path, int flags,
-		         tree *fn_p, tsubst_flags_t complain)
+		         tree *fn_p, tsubst_flags_t complain, location_t loc)
 {
   struct z_candidate *candidates = 0, *cand;
   tree explicit_targs = NULL_TREE;
@@ -7469,7 +7473,7 @@ build_new_method_call_1 (tree instance,
 	      if (fn_p)
 		*fn_p = fn;
 	      /* Build the actual CALL_EXPR.  */
-	      call = build_over_call (cand, flags, complain);
+	      call = build_over_call (cand, flags, complain, loc);
 	      /* In an expression of the form `a->f()' where `f' turns
 		 out to be a static member function, `a' is
 		 none-the-less evaluated.  */
@@ -7534,12 +7538,12 @@ build_new_method_call_1 (tree instance,
 tree
 build_new_method_call (tree instance, tree fns, VEC(tree,gc) **args,
 		       tree conversion_path, int flags,
-		       tree *fn_p, tsubst_flags_t complain)
+		       tree *fn_p, tsubst_flags_t complain, location_t loc)
 {
   tree ret;
   bool subtime = timevar_cond_start (TV_OVERLOAD);
   ret = build_new_method_call_1 (instance, fns, args, conversion_path, flags,
-                                 fn_p, complain);
+                                 fn_p, complain, loc);
   timevar_cond_stop (TV_OVERLOAD, subtime);
   return ret;
 }
@@ -8562,6 +8566,7 @@ tree
 perform_implicit_conversion_flags (tree type, tree expr,
 				   tsubst_flags_t complain, int flags)
 {
+  tree result = expr;
   conversion *conv;
   void *p;
   location_t loc = EXPR_LOC_OR_HERE (expr);
@@ -8605,6 +8610,9 @@ perform_implicit_conversion_flags (tree
     }
   else
     expr = convert_like (conv, expr, complain);
+  
+  if (expr != error_mark_node)
+    duplicate_expr_locations (expr, result);
 
   /* Free all the conversions we allocated.  */
   obstack_free (&conversion_obstack, p);
Index: cp/method.c
===================================================================
--- cp/method.c	(revision 190939)
+++ cp/method.c	(working copy)
@@ -837,7 +837,8 @@ locate_fn_flags (tree type, tree name, t
     }
 
   fns = lookup_fnfields (binfo, name, 0);
-  rval = build_new_method_call (ob, fns, &args, binfo, flags, &fn, complain);
+  rval = build_new_method_call (ob, fns, &args, binfo, flags, &fn, complain,
+				input_location);
 
   release_tree_vector (args);
   if (fn && rval == error_mark_node)
Index: cp/class.c
===================================================================
--- cp/class.c	(revision 190939)
+++ cp/class.c	(working copy)
@@ -7355,6 +7355,9 @@ build_self_reference (void)
   DECL_CONTEXT (value) = current_class_type;
   DECL_ARTIFICIAL (value) = 1;
   SET_DECL_SELF_REFERENCE_P (value);
+  DECL_SOURCE_LOCATION (value) =
+    DECL_SOURCE_LOCATION (TYPE_NAME (current_class_type));
+
   set_underlying_type (value);
 
   if (processing_template_decl)
Index: cp/pt.c
===================================================================
--- cp/pt.c	(revision 190939)
+++ cp/pt.c	(working copy)
@@ -13788,20 +13788,20 @@ tsubst_copy_and_build (tree t,
 	      ret = finish_call_expr (function, &call_args,
 				       /*disallow_virtual=*/false,
 				       /*koenig_p=*/false,
-				       complain);
+				       complain, input_location);
 	    else
 	      ret = (build_new_method_call
 		      (instance, fn,
 		       &call_args, NULL_TREE,
 		       qualified_p ? LOOKUP_NONVIRTUAL : LOOKUP_NORMAL,
 		       /*fn_p=*/NULL,
-		       complain));
+		       complain, input_location));
 	  }
 	else
 	  ret = finish_call_expr (function, &call_args,
 				  /*disallow_virtual=*/qualified_p,
 				  koenig_p,
-				  complain);
+				  complain, input_location);
 
 	release_tree_vector (call_args);
 
Index: cp/parser.c
===================================================================
--- cp/parser.c	(revision 190939)
+++ cp/parser.c	(working copy)
@@ -1787,7 +1787,7 @@ static bool cp_parser_translation_unit
 static tree cp_parser_primary_expression
   (cp_parser *, bool, bool, bool, cp_id_kind *);
 static tree cp_parser_id_expression
-  (cp_parser *, bool, bool, bool *, bool, bool);
+  (cp_parser *, bool, bool, bool *, bool, bool, location_t *);
 static tree cp_parser_unqualified_id
   (cp_parser *, bool, bool, bool, bool);
 static tree cp_parser_nested_name_specifier_opt
@@ -1803,7 +1803,7 @@ static tree cp_parser_postfix_open_squar
 static tree cp_parser_postfix_dot_deref_expression
   (cp_parser *, enum cpp_ttype, tree, bool, cp_id_kind *, location_t);
 static VEC(tree,gc) *cp_parser_parenthesized_expression_list
-  (cp_parser *, int, bool, bool, bool *);
+  (cp_parser *, int, bool, bool, bool *, location_t *, int *);
 /* 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
@@ -1831,7 +1831,7 @@ static tree cp_parser_cast_expression
 static tree cp_parser_binary_expression
   (cp_parser *, bool, bool, enum cp_parser_prec, cp_id_kind *);
 static tree cp_parser_question_colon_clause
-  (cp_parser *, tree);
+  (cp_parser *, tree, location_t);
 static tree cp_parser_assignment_expression
   (cp_parser *, bool, cp_id_kind *);
 static enum tree_code cp_parser_assignment_operator_opt
@@ -1968,7 +1968,7 @@ static cp_virt_specifiers cp_parser_virt
 static tree cp_parser_late_return_type_opt
   (cp_parser *, cp_cv_quals);
 static tree cp_parser_declarator_id
-  (cp_parser *, bool);
+  (cp_parser *, bool, location_t *);
 static tree cp_parser_type_id
   (cp_parser *);
 static tree cp_parser_template_type_arg
@@ -2844,7 +2844,8 @@ cp_parser_parse_and_diagnose_invalid_typ
 				/*check_dependency_p=*/true,
 				/*template_p=*/NULL,
 				/*declarator_p=*/true,
-				/*optional_p=*/false);
+				/*optional_p=*/false,
+				/*loc=*/NULL);
   /* If the next token is a (, this is a function with no explicit return
      type, i.e. constructor, destructor or conversion op.  */
   if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)
@@ -3583,7 +3584,8 @@ cp_parser_userdef_char_literal (cp_parse
       release_tree_vector (args);
       return error_mark_node;
     }
-  result = finish_call_expr (decl, &args, false, true, tf_warning_or_error);
+  result = finish_call_expr
+	     (decl, &args, false, true, tf_warning_or_error, token->location);
   release_tree_vector (args);
   if (result != error_mark_node)
     return result;
@@ -3641,7 +3643,8 @@ cp_parser_userdef_numeric_literal (cp_pa
   decl = lookup_literal_operator (name, args);
   if (decl && decl != error_mark_node)
     {
-      result = finish_call_expr (decl, &args, false, true, tf_none);
+      result = finish_call_expr
+		 (decl, &args, false, true, tf_none, token->location);
       if (result != error_mark_node)
 	{
 	  release_tree_vector (args);
@@ -3658,7 +3661,8 @@ cp_parser_userdef_numeric_literal (cp_pa
   decl = lookup_literal_operator (name, args);
   if (decl && decl != error_mark_node)
     {
-      result = finish_call_expr (decl, &args, false, true, tf_none);
+      result = finish_call_expr
+		 (decl, &args, false, true, tf_none, token->location);
       if (result != error_mark_node)
 	{
 	  release_tree_vector (args);
@@ -3676,7 +3680,8 @@ cp_parser_userdef_numeric_literal (cp_pa
     {
       tree tmpl_args = make_char_string_pack (num_string);
       decl = lookup_template_function (decl, tmpl_args);
-      result = finish_call_expr (decl, &args, false, true, tf_none);
+      result = finish_call_expr
+		 (decl, &args, false, true, tf_none, token->location);
       if (result != error_mark_node)
 	{
 	  release_tree_vector (args);
@@ -3716,7 +3721,7 @@ cp_parser_userdef_string_literal (cp_tok
       release_tree_vector (args);
       return error_mark_node;
     }
-  result = finish_call_expr (decl, &args, false, true, tf_none);
+  result = finish_call_expr (decl, &args, false, true, tf_none, token->location);
   release_tree_vector (args);
   if (result != error_mark_node)
     return result;
@@ -4226,7 +4231,8 @@ cp_parser_primary_expression (cp_parser
 				     /*check_dependency_p=*/true,
 				     &template_p,
 				     /*declarator_p=*/false,
-				     /*optional_p=*/false);
+				     /*optional_p=*/false,
+				     /*loc=*/NULL);
 	if (id_expression == error_mark_node)
 	  return error_mark_node;
 	id_expr_token = token;
@@ -4389,7 +4395,10 @@ cp_parser_primary_expression (cp_parser
    named is a template.
 
    If DECLARATOR_P is true, the id-expression is appearing as part of
-   a declarator, rather than as part of an expression.  */
+   a declarator, rather than as part of an expression.
+
+   If LOC is non-NULL and a qualified-id is found, LOC is set to the location
+   of the unqualified-id.  */
 
 static tree
 cp_parser_id_expression (cp_parser *parser,
@@ -4397,7 +4406,8 @@ cp_parser_id_expression (cp_parser *pars
 			 bool check_dependency_p,
 			 bool *template_p,
 			 bool declarator_p,
-			 bool optional_p)
+			 bool optional_p,
+			 location_t *loc)
 {
   bool global_scope_p;
   bool nested_name_specifier_p;
@@ -4437,6 +4447,10 @@ cp_parser_id_expression (cp_parser *pars
       saved_scope = parser->scope;
       saved_object_scope = parser->object_scope;
       saved_qualifying_scope = parser->qualifying_scope;
+
+      if (loc)
+	*loc = cp_lexer_peek_token (parser->lexer)->location;
+
       /* Process the final unqualified-id.  */
       unqualified_id = cp_parser_unqualified_id (parser, *template_p,
 						 check_dependency_p,
@@ -4459,6 +4473,9 @@ cp_parser_id_expression (cp_parser *pars
       /* Peek at the next token.  */
       token = cp_lexer_peek_token (parser->lexer);
 
+      if (loc)
+	*loc = token->location;
+
       /* If it's an identifier, and the next token is not a "<", then
 	 we can avoid the template-id case.  This is an optimization
 	 for this common case.  */
@@ -4498,10 +4515,15 @@ cp_parser_id_expression (cp_parser *pars
 	}
     }
   else
-    return cp_parser_unqualified_id (parser, template_keyword_p,
-				     /*check_dependency_p=*/true,
-				     declarator_p,
-				     optional_p);
+    {
+      if (loc)
+	*loc = cp_lexer_peek_token (parser->lexer)->location;
+
+      return cp_parser_unqualified_id (parser, template_keyword_p,
+				       /*check_dependency_p=*/true,
+				       declarator_p,
+				       optional_p);
+    }
 }
 
 /* Parse an unqualified-id.
@@ -5326,6 +5348,7 @@ cp_parser_postfix_expression (cp_parser
   cp_id_kind idk = CP_ID_KIND_NONE;
   tree postfix_expression = NULL_TREE;
   bool is_member_access = false;
+  location_t locs[64], member_loc = UNKNOWN_LOCATION;
 
   /* Peek at the next token.  */
   token = cp_lexer_peek_token (parser->lexer);
@@ -5468,7 +5491,8 @@ cp_parser_postfix_expression (cp_parser
 	cp_lexer_consume_token (parser->lexer);
 	vec = cp_parser_parenthesized_expression_list (parser, non_attr,
 		    /*cast_p=*/false, /*allow_expansion_p=*/true,
-		    /*non_constant_p=*/NULL);
+		    /*non_constant_p=*/NULL,
+		    /*locs=*/NULL, /*num_locs=*/NULL);
 	if (vec == NULL)
 	  return error_mark_node;
 
@@ -5603,6 +5627,7 @@ cp_parser_postfix_expression (cp_parser
 	  = unqualified_name_lookup_error (postfix_expression);
 
       /* Peek at the next token.  */
+      locs[0] = token->location;
       token = cp_lexer_peek_token (parser->lexer);
 
       switch (token->type)
@@ -5624,6 +5649,7 @@ cp_parser_postfix_expression (cp_parser
 	    bool saved_integral_constant_expression_p = false;
 	    bool saved_non_integral_constant_expression_p = false;
 	    VEC(tree,gc) *args;
+	    int num_locs = 63;
 
             is_member_access = false;
 
@@ -5642,7 +5668,7 @@ cp_parser_postfix_expression (cp_parser
 	    args = (cp_parser_parenthesized_expression_list
 		    (parser, non_attr,
 		     /*cast_p=*/false, /*allow_expansion_p=*/true,
-		     /*non_constant_p=*/NULL));
+		     /*non_constant_p=*/NULL, &locs[1], &num_locs));
 	    if (is_builtin_constant_p)
 	      {
 		parser->integral_constant_expression_p
@@ -5717,6 +5743,11 @@ cp_parser_postfix_expression (cp_parser
 		tree instance = TREE_OPERAND (postfix_expression, 0);
 		tree fn = TREE_OPERAND (postfix_expression, 1);
 
+		if (member_loc == UNKNOWN_LOCATION)
+		  member_loc = locs[0];
+		else
+		  locs[0] = member_loc;
+
 		if (processing_template_decl
 		    && (type_dependent_expression_p (instance)
 			|| (!BASELINK_P (fn)
@@ -5739,14 +5770,14 @@ cp_parser_postfix_expression (cp_parser
 			 ? LOOKUP_NORMAL|LOOKUP_NONVIRTUAL
 			 : LOOKUP_NORMAL),
 			/*fn_p=*/NULL,
-			tf_warning_or_error));
+			tf_warning_or_error, member_loc));
 		  }
 		else
 		  postfix_expression
 		    = finish_call_expr (postfix_expression, &args,
 					/*disallow_virtual=*/false,
 					/*koenig_p=*/false,
-					tf_warning_or_error);
+					tf_warning_or_error, member_loc);
 	      }
 	    else if (TREE_CODE (postfix_expression) == OFFSET_REF
 		     || TREE_CODE (postfix_expression) == MEMBER_REF
@@ -5760,14 +5791,15 @@ cp_parser_postfix_expression (cp_parser
 		= finish_call_expr (postfix_expression, &args,
 				    /*disallow_virtual=*/true,
 				    koenig_p,
-				    tf_warning_or_error);
+				    tf_warning_or_error, locs[0]);
 	    else
 	      /* All other function calls.  */
 	      postfix_expression
 		= finish_call_expr (postfix_expression, &args,
 				    /*disallow_virtual=*/false,
 				    koenig_p,
-				    tf_warning_or_error);
+				    tf_warning_or_error, locs[0]);
+	    SET_EXPR_LOCATIONS (postfix_expression, locs, num_locs+1);
 
 	    /* The POSTFIX_EXPRESSION is certainly no longer an id.  */
 	    idk = CP_ID_KIND_NONE;
@@ -5785,6 +5817,7 @@ cp_parser_postfix_expression (cp_parser
 
 	  /* Consume the `.' or `->' operator.  */
 	  cp_lexer_consume_token (parser->lexer);
+	  member_loc = locs[1] = cp_lexer_peek_token (parser->lexer)->location;
 
 	  postfix_expression
 	    = cp_parser_postfix_dot_deref_expression (parser, token->type,
@@ -5792,6 +5825,8 @@ cp_parser_postfix_expression (cp_parser
 						      false, &idk,
 						      token->location);
 
+	  protected_set_expr_location (postfix_expression, token->location);
+	  SET_EXPR_LOCATIONS (postfix_expression, locs, 2);
           is_member_access = true;
 	  break;
 
@@ -5799,6 +5834,7 @@ cp_parser_postfix_expression (cp_parser
 	  /* postfix-expression ++  */
 	  /* Consume the `++' token.  */
 	  cp_lexer_consume_token (parser->lexer);
+	  locs[1] = locs [0];
 	  /* Generate a representation for the complete expression.  */
 	  postfix_expression
 	    = finish_increment_expr (postfix_expression,
@@ -5806,6 +5842,11 @@ cp_parser_postfix_expression (cp_parser
 	  /* Increments may not appear in constant-expressions.  */
 	  if (cp_parser_non_integral_constant_expression (parser, NIC_INC))
 	    postfix_expression = error_mark_node;
+	  else
+	    {
+	      protected_set_expr_location (postfix_expression, token->location);
+	      SET_EXPR_LOCATIONS (postfix_expression, locs, 2);
+	    }
 	  idk = CP_ID_KIND_NONE;
           is_member_access = false;
 	  break;
@@ -5814,6 +5855,7 @@ cp_parser_postfix_expression (cp_parser
 	  /* postfix-expression -- */
 	  /* Consume the `--' token.  */
 	  cp_lexer_consume_token (parser->lexer);
+	  locs[1] = locs [0];
 	  /* Generate a representation for the complete expression.  */
 	  postfix_expression
 	    = finish_increment_expr (postfix_expression,
@@ -5821,6 +5863,11 @@ cp_parser_postfix_expression (cp_parser
 	  /* Decrements may not appear in constant-expressions.  */
 	  if (cp_parser_non_integral_constant_expression (parser, NIC_DEC))
 	    postfix_expression = error_mark_node;
+	  else
+	    {
+	      protected_set_expr_location (postfix_expression, token->location);
+	      SET_EXPR_LOCATIONS (postfix_expression, locs, 2);
+	    }
 	  idk = CP_ID_KIND_NONE;
           is_member_access = false;
 	  break;
@@ -5855,10 +5902,13 @@ cp_parser_postfix_open_square_expression
 					  bool for_offsetof)
 {
   tree index;
+  location_t locs[2];
   location_t loc = cp_lexer_peek_token (parser->lexer)->location;
 
+  locs[0] = input_location;
   /* Consume the `[' token.  */
   cp_lexer_consume_token (parser->lexer);
+  locs[1] = cp_lexer_peek_token (parser->lexer)->location;
 
   /* Parse the index expression.  */
   /* ??? For offsetof, there is a question of what to allow here.  If
@@ -5893,6 +5943,10 @@ cp_parser_postfix_open_square_expression
   if (!for_offsetof
       && (cp_parser_non_integral_constant_expression (parser, NIC_ARRAY_REF)))
     postfix_expression = error_mark_node;
+  else if (TREE_CODE (postfix_expression) == INDIRECT_REF)
+    SET_EXPR_LOCATIONS (TREE_OPERAND (postfix_expression, 0), locs, 2);
+  else
+    SET_EXPR_LOCATIONS (postfix_expression, locs, 2);
 
   return postfix_expression;
 }
@@ -6018,7 +6072,8 @@ cp_parser_postfix_dot_deref_expression (
 	       /*check_dependency_p=*/true,
 	       &template_p,
 	       /*declarator_p=*/false,
-	       /*optional_p=*/false));
+	       /*optional_p=*/false,
+	       /*loc=*/NULL));
       /* In general, build a SCOPE_REF if the member name is qualified.
 	 However, if the name was not dependent and has already been
 	 resolved; there is no need to build the SCOPE_REF.  For example;
@@ -6115,12 +6170,14 @@ cp_parser_parenthesized_expression_list
 					 int is_attribute_list,
 					 bool cast_p,
                                          bool allow_expansion_p,
-					 bool *non_constant_p)
+					 bool *non_constant_p,
+					 location_t *locs, int *num_locs)
 {
   VEC(tree,gc) *expression_list;
   bool fold_expr_p = is_attribute_list != non_attr;
   tree identifier = NULL_TREE;
   bool saved_greater_than_is_operator_p;
+  int i = 0;
 
   /* Assume all the expressions will be constant.  */
   if (non_constant_p)
@@ -6143,6 +6200,9 @@ cp_parser_parenthesized_expression_list
       {
 	tree expr;
 
+	if (locs && i < *num_locs)
+	  locs[i++] = cp_lexer_peek_token (parser->lexer)->location;
+
 	/* At the beginning of attribute lists, check to see if the
 	   next token is an identifier.  */
 	if (is_attribute_list == id_attr
@@ -6238,6 +6298,9 @@ cp_parser_parenthesized_expression_list
 	}
     }
 
+  if (locs)
+    *num_locs = i;
+
   parser->greater_than_is_operator_p
     = saved_greater_than_is_operator_p;
 
@@ -6540,9 +6603,11 @@ cp_parser_unary_expression (cp_parser *p
 	  tree identifier;
 	  tree expression;
 	  location_t loc = token->location;
+	  location_t loc2;
 
 	  /* Consume the '&&' token.  */
 	  cp_lexer_consume_token (parser->lexer);
+	  loc2 = cp_lexer_peek_token (parser->lexer)->location;
 	  /* Look for the identifier.  */
 	  identifier = cp_parser_identifier (parser);
 	  /* Create an expression representing the address.  */
@@ -6550,6 +6615,8 @@ cp_parser_unary_expression (cp_parser *p
 	  if (cp_parser_non_integral_constant_expression (parser,
 							  NIC_ADDR_LABEL))
 	    expression = error_mark_node;
+	  else
+	    SET_EXPR_LOCATION2 (expression, loc2);
 	  return expression;
 	}
     }
@@ -6559,9 +6626,11 @@ cp_parser_unary_expression (cp_parser *p
       tree expression = error_mark_node;
       non_integral_constant non_constant_p = NIC_NONE;
       location_t loc = token->location;
+      location_t loc2;
 
       /* Consume the operator token.  */
       token = cp_lexer_consume_token (parser->lexer);
+      loc2 = cp_lexer_peek_token (parser->lexer)->location;
       /* Parse the cast-expression.  */
       cast_expression
 	= cp_parser_cast_expression (parser,
@@ -6606,6 +6675,8 @@ cp_parser_unary_expression (cp_parser *p
 	  && cp_parser_non_integral_constant_expression (parser,
 							 non_constant_p))
 	expression = error_mark_node;
+      else
+	SET_EXPR_LOCATION2 (expression, loc2);
 
       return expression;
     }
@@ -6761,7 +6832,7 @@ cp_parser_new_placement (cp_parser* pars
   expression_list = (cp_parser_parenthesized_expression_list
 		     (parser, non_attr, /*cast_p=*/false,
 		      /*allow_expansion_p=*/true,
-		      /*non_constant_p=*/NULL));
+		      /*non_constant_p=*/NULL, /*locs=*/NULL, /*num_locs=*/NULL));
 
   return expression_list;
 }
@@ -6963,7 +7034,7 @@ cp_parser_new_initializer (cp_parser* pa
     expression_list = (cp_parser_parenthesized_expression_list
 		       (parser, non_attr, /*cast_p=*/false,
 			/*allow_expansion_p=*/true,
-			/*non_constant_p=*/NULL));
+			/*non_constant_p=*/NULL, /*locs=*/NULL, /*num_locs=*/NULL));
 
   return expression_list;
 }
@@ -7281,10 +7352,14 @@ cp_parser_binary_expression (cp_parser*
   enum tree_code rhs_type;
   enum cp_parser_prec new_prec, lookahead_prec;
   tree overload;
+  location_t locs[2];
 
   /* Parse the first expression.  */
+  locs[0] = cp_lexer_peek_token (parser->lexer)->location;
   current.lhs = cp_parser_cast_expression (parser, /*address_p=*/false,
 					   cast_p, pidk);
+  if (EXPR_P (current.lhs))
+    locs[0] = EXPR_LOCATION (current.lhs);
   current.lhs_type = ERROR_MARK;
   current.prec = prec;
 
@@ -7340,6 +7415,7 @@ cp_parser_binary_expression (cp_parser*
 
       /* Extract another operand.  It may be the RHS of this expression
 	 or the LHS of a new, higher priority expression.  */
+      locs[1] = cp_lexer_peek_token (parser->lexer)->location;
       rhs = cp_parser_simple_cast_expression (parser);
       rhs_type = ERROR_MARK;
 
@@ -7359,6 +7435,8 @@ cp_parser_binary_expression (cp_parser*
 	  current.lhs = rhs;
 	  current.lhs_type = rhs_type;
 	  current.prec = new_prec;
+	  current.loc = locs[0];
+	  locs[0] = locs[1];
 	  new_prec = lookahead_prec;
 	  goto get_rhs;
 
@@ -7375,6 +7453,8 @@ cp_parser_binary_expression (cp_parser*
 	  rhs_type = current.lhs_type;
 	  --sp;
 	  current = *sp;
+	  locs[1] = locs[0];
+	  locs[0] = sp->loc;
 	}
 
       /* Undo the disabling of warnings done above.  */
@@ -7402,6 +7482,7 @@ cp_parser_binary_expression (cp_parser*
 					 current.lhs, current.lhs_type,
 					 rhs, rhs_type, &overload,
 					 tf_warning_or_error);
+      SET_EXPR_LOCATIONS (current.lhs, locs, 2);
       current.lhs_type = current.tree_type;
 
       /* If the binary operator required the use of an overloaded operator,
@@ -7423,6 +7504,7 @@ cp_parser_binary_expression (cp_parser*
 /* Parse the `? expression : assignment-expression' part of a
    conditional-expression.  The LOGICAL_OR_EXPR is the
    logical-or-expression that started the conditional-expression.
+   LHS_LOC is the location of the lhs.
    Returns a representation of the entire conditional-expression.
 
    This routine is used by cp_parser_assignment_expression.
@@ -7434,13 +7516,24 @@ cp_parser_binary_expression (cp_parser*
      ? : assignment-expression */
 
 static tree
-cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr)
+cp_parser_question_colon_clause (cp_parser* parser,
+				 tree logical_or_expr,
+				 location_t lhs_loc)
 {
   tree expr;
   tree assignment_expr;
+  tree result;
+  /* locations of the conditional expression:
+     locs[0] == location of ?
+     locs[1] == location of lhs
+     locs[2] == location of expression
+     locs[3] == location of assignment-expression.  */
+  location_t locs[4];
   struct cp_token *token;
   location_t loc = cp_lexer_peek_token (parser->lexer)->location;
 
+  locs[0] = cp_lexer_peek_token (parser->lexer)->location;
+  locs[1] = lhs_loc;
   /* Consume the `?' token.  */
   cp_lexer_consume_token (parser->lexer);
   token = cp_lexer_peek_token (parser->lexer);
@@ -7451,6 +7544,7 @@ cp_parser_question_colon_clause (cp_pars
                "ISO C++ does not allow ?: with omitted middle operand");
       /* Implicit true clause.  */
       expr = NULL_TREE;
+      locs[2] = UNKNOWN_LOCATION;
       c_inhibit_evaluation_warnings += logical_or_expr == truthvalue_true_node;
       warn_for_omitted_condop (token->location, logical_or_expr);
     }
@@ -7458,6 +7552,7 @@ cp_parser_question_colon_clause (cp_pars
     {
       bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p;
       parser->colon_corrects_to_scope_p = false;
+      locs[2] = cp_lexer_peek_token (parser->lexer)->location;
       /* Parse the expression.  */
       c_inhibit_evaluation_warnings += logical_or_expr == truthvalue_false_node;
       expr = cp_parser_expression (parser, /*cast_p=*/false, NULL);
@@ -7469,15 +7564,19 @@ cp_parser_question_colon_clause (cp_pars
 
   /* The next token should be a `:'.  */
   cp_parser_require (parser, CPP_COLON, RT_COLON);
+  locs[3] = cp_lexer_peek_token (parser->lexer)->location;
   /* Parse the assignment-expression.  */
   assignment_expr = cp_parser_assignment_expression (parser, /*cast_p=*/false, NULL);
   c_inhibit_evaluation_warnings -= logical_or_expr == truthvalue_true_node;
 
   /* Build the conditional-expression.  */
-  return build_x_conditional_expr (loc, logical_or_expr,
-				   expr,
-				   assignment_expr,
-                                   tf_warning_or_error);
+  result = build_x_conditional_expr (loc, logical_or_expr,
+				     expr,
+				     assignment_expr,
+                                     tf_warning_or_error);
+  protected_set_expr_location (result, locs[0]);
+  SET_EXPR_LOCATIONS (result, &locs[1], 3);
+  return result;
 }
 
 /* Parse an assignment-expression.
@@ -7496,6 +7595,12 @@ cp_parser_assignment_expression (cp_pars
 				 cp_id_kind * pidk)
 {
   tree expr;
+  /* extra locations of the assignment expression:
+     locs[0] == location of lhs
+     locs[1] == location of rhs.  */
+  location_t locs[2];
+
+  locs[0] = cp_lexer_peek_token (parser->lexer)->location;
 
   /* If the next token is the `throw' keyword, then we're looking at
      a throw-expression.  */
@@ -7511,7 +7616,7 @@ cp_parser_assignment_expression (cp_pars
       /* If the next token is a `?' then we're actually looking at a
 	 conditional-expression.  */
       if (cp_lexer_next_token_is (parser->lexer, CPP_QUERY))
-	return cp_parser_question_colon_clause (parser, expr);
+	return cp_parser_question_colon_clause (parser, expr, locs[0]);
       else
 	{
 	  location_t loc = cp_lexer_peek_token (parser->lexer)->location;
@@ -7524,9 +7629,11 @@ cp_parser_assignment_expression (cp_pars
 	    {
 	      bool non_constant_p;
 	      location_t saved_input_location;
+	      tree rhs;
 
 	      /* Parse the right-hand side of the assignment.  */
-	      tree rhs = cp_parser_initializer_clause (parser, &non_constant_p);
+	      locs[1] = cp_lexer_peek_token (parser->lexer)->location;
+	      rhs = cp_parser_initializer_clause (parser, &non_constant_p);
 
 	      if (BRACE_ENCLOSED_INITIALIZER_P (rhs))
 		maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS);
@@ -7545,6 +7652,7 @@ cp_parser_assignment_expression (cp_pars
 					  rhs,
 					  tf_warning_or_error);
 	      input_location = saved_input_location;
+	      SET_EXPR_LOCATIONS (expr, locs, 2);
 	    }
 	}
     }
@@ -9609,12 +9717,12 @@ cp_parser_perform_range_for_lookup (tree
 						/*include_std=*/true,
 						tf_warning_or_error);
 	  *begin = finish_call_expr (member_begin, &vec, false, true,
-				     tf_warning_or_error);
+				     tf_warning_or_error, input_location);
 	  member_end = perform_koenig_lookup (id_end, vec,
 					      /*include_std=*/true,
 					      tf_warning_or_error);
 	  *end = finish_call_expr (member_end, &vec, false, true,
-				   tf_warning_or_error);
+				   tf_warning_or_error, input_location);
 
 	  release_tree_vector (vec);
 	}
@@ -9658,7 +9766,7 @@ cp_parser_range_for_member_function (tre
   res = finish_call_expr (member, &vec,
 			  /*disallow_virtual=*/false,
 			  /*koenig_p=*/false,
-			  tf_warning_or_error);
+			  tf_warning_or_error, input_location);
   release_tree_vector (vec);
   return res;
 }
@@ -11067,7 +11175,8 @@ cp_parser_decltype (cp_parser *parser)
                                   /*check_dependency_p=*/true,
                                   /*template_p=*/NULL,
                                   /*declarator_p=*/false,
-                                  /*optional_p=*/false);
+                                  /*optional_p=*/false,
+				  /*loc=*/NULL);
 
   if (!cp_parser_error_occurred (parser) && expr != error_mark_node)
     {
@@ -11513,7 +11622,9 @@ cp_parser_mem_initializer (cp_parser* pa
       vec = cp_parser_parenthesized_expression_list (parser, non_attr,
 						     /*cast_p=*/false,
 						     /*allow_expansion_p=*/true,
-						     /*non_constant_p=*/NULL);
+						     /*non_constant_p=*/NULL,
+						     /*locs=*/NULL,
+						     /*num_locs=*/NULL);
       if (vec == NULL)
 	return error_mark_node;
       expression_list = build_tree_list_vec (vec);
@@ -12315,7 +12426,8 @@ cp_parser_type_parameter (cp_parser* par
 					 /*check_dependency_p=*/true,
 					 /*template_p=*/&is_template,
 					 /*declarator_p=*/false,
-					 /*optional_p=*/false);
+					 /*optional_p=*/false,
+					 /*loc=*/NULL);
 	    if (TREE_CODE (default_argument) == TYPE_DECL)
 	      /* If the id-expression was a template-id that refers to
 		 a template-class, we already have the declaration here,
@@ -12943,7 +13055,8 @@ cp_parser_template_argument (cp_parser*
 				      /*check_dependency_p=*/true,
 				      &template_p,
 				      /*declarator_p=*/false,
-				      /*optional_p=*/false);
+				      /*optional_p=*/false,
+				      /*loc=*/NULL);
   /* If the next token isn't a `,' or a `>', then this argument wasn't
      really finished.  */
   if (!cp_parser_next_token_ends_template_argument_p (parser))
@@ -16245,6 +16358,7 @@ cp_parser_direct_declarator (cp_parser*
 	    bool abstract_ok;
 	    bool pack_expansion_p = false;
 	    cp_token *declarator_id_start_token;
+	    location_t loc;
 
 	    /* Parse a declarator-id */
 	    abstract_ok = (dcl_kind == CP_PARSER_DECLARATOR_EITHER);
@@ -16265,7 +16379,8 @@ cp_parser_direct_declarator (cp_parser*
 
 	    declarator_id_start_token = cp_lexer_peek_token (parser->lexer);
 	    unqualified_name
-	      = cp_parser_declarator_id (parser, /*optional_p=*/abstract_ok);
+	      = cp_parser_declarator_id (parser, /*optional_p=*/abstract_ok,
+					 &loc);
 	    qualifying_scope = parser->scope;
 	    if (abstract_ok)
 	      {
@@ -16421,7 +16536,7 @@ cp_parser_direct_declarator (cp_parser*
 	    declarator = make_id_declarator (qualifying_scope,
 					     unqualified_name,
 					     sfk);
-	    declarator->id_loc = token->location;
+	    declarator->id_loc = loc;
 	    declarator->parameter_pack_p = pack_expansion_p;
 
 	    if (pack_expansion_p)
@@ -16772,10 +16887,12 @@ cp_parser_late_return_type_opt (cp_parse
    If the id-expression was a qualified-id, then a SCOPE_REF is
    returned.  The first operand is the scope (either a NAMESPACE_DECL
    or TREE_TYPE), but the second is still just a representation of an
-   unqualified-id.  */
+   unqualified-id.
+
+   if LOC is not-NULL, it is set to the location of the unqualified-id.  */
 
 static tree
-cp_parser_declarator_id (cp_parser* parser, bool optional_p)
+cp_parser_declarator_id (cp_parser* parser, bool optional_p, location_t *loc)
 {
   tree id;
   /* The expression must be an id-expression.  Assume that qualified
@@ -16797,7 +16914,8 @@ cp_parser_declarator_id (cp_parser* pars
 				/*check_dependency_p=*/false,
 				/*template_p=*/NULL,
 				/*declarator_p=*/true,
-				optional_p);
+				optional_p,
+				loc);
   if (id && BASELINK_P (id))
     id = BASELINK_FUNCTIONS (id);
   return id;
@@ -17548,7 +17666,9 @@ cp_parser_initializer (cp_parser* parser
       vec = cp_parser_parenthesized_expression_list (parser, non_attr,
 						     /*cast_p=*/false,
 						     /*allow_expansion_p=*/true,
-						     non_constant_p);
+						     non_constant_p,
+						     /*locs=*/NULL,
+						     /*num_locs=*/NULL);
       if (vec == NULL)
 	return error_mark_node;
       init = build_tree_list_vec (vec);
@@ -20223,7 +20343,7 @@ cp_parser_attribute_list (cp_parser* par
 	      vec = cp_parser_parenthesized_expression_list
 		    (parser, attr_flag, /*cast_p=*/false,
 		     /*allow_expansion_p=*/false,
-		     /*non_constant_p=*/NULL);
+		     /*non_constant_p=*/NULL, /*locs=*/NULL, /*num_locs=*/NULL);
 	      if (vec == NULL)
 		arguments = error_mark_node;
 	      else
@@ -21489,7 +21609,9 @@ cp_parser_functional_cast (cp_parser* pa
   vec = cp_parser_parenthesized_expression_list (parser, non_attr,
 						 /*cast_p=*/true,
 						 /*allow_expansion_p=*/true,
-						 /*non_constant_p=*/NULL);
+						 /*non_constant_p=*/NULL,
+						 /*locs=*/NULL,
+						 /*num_locs=*/NULL);
   if (vec == NULL)
     expression_list = error_mark_node;
   else
@@ -25145,7 +25267,8 @@ cp_parser_omp_var_list_no_open (cp_parse
 				      /*check_dependency_p=*/true,
 				      /*template_p=*/NULL,
 				      /*declarator_p=*/false,
-				      /*optional_p=*/false);
+				      /*optional_p=*/false,
+				      /*loc=*/NULL);
       if (name == error_mark_node)
 	goto skip_comma;
 
Index: cp/init.c
===================================================================
--- cp/init.c	(revision 190939)
+++ cp/init.c	(working copy)
@@ -2482,7 +2482,7 @@ build_new_1 (VEC(tree,gc) **placement, t
 					      /*conversion_path=*/NULL_TREE,
 					      LOOKUP_NORMAL,
 					      &alloc_fn,
-					      complain);
+					      complain, input_location);
 	}
       else
 	{
@@ -3693,7 +3693,7 @@ build_dtor_call (tree exp, special_funct
 				/*conversion_path=*/NULL_TREE,
 				flags,
 				/*fn_p=*/NULL,
-				complain);
+				complain, input_location);
 }
 
 /* Generate a call to a destructor. TYPE is the type to cast ADDR to.
Index: cp/cp-tree.h
===================================================================
--- cp/cp-tree.h	(revision 190939)
+++ cp/cp-tree.h	(working copy)
@@ -4890,7 +4890,7 @@ extern tree build_operator_new_call		(tr
 						 tsubst_flags_t);
 extern tree build_new_method_call		(tree, tree, VEC(tree,gc) **,
 						 tree, int, tree *,
-						 tsubst_flags_t);
+						 tsubst_flags_t, location_t);
 extern tree build_special_member_call		(tree, tree, VEC(tree,gc) **,
 						 tree, int, tsubst_flags_t);
 extern tree build_new_op			(location_t, enum tree_code,
@@ -5594,7 +5594,8 @@ bool empty_expr_stmt_p				(tree);
 extern tree perform_koenig_lookup		(tree, VEC(tree,gc) *, bool,
 						 tsubst_flags_t);
 extern tree finish_call_expr			(tree, VEC(tree,gc) **, bool,
-						 bool, tsubst_flags_t);
+						 bool, tsubst_flags_t,
+						 location_t);
 extern tree finish_increment_expr		(tree, enum tree_code);
 extern tree finish_this_expr			(void);
 extern tree finish_pseudo_destructor_expr       (tree, tree, tree);
Index: cp/semantics.c
===================================================================
--- cp/semantics.c	(revision 190939)
+++ cp/semantics.c	(working copy)
@@ -2047,7 +2047,7 @@ perform_koenig_lookup (tree fn, VEC(tree
 
 tree
 finish_call_expr (tree fn, VEC(tree,gc) **args, bool disallow_virtual,
-		  bool koenig_p, tsubst_flags_t complain)
+		  bool koenig_p, tsubst_flags_t complain, location_t loc)
 {
   tree result;
   tree orig_fn;
@@ -2117,7 +2117,7 @@ finish_call_expr (tree fn, VEC(tree,gc)
                                          ? LOOKUP_NORMAL | LOOKUP_NONVIRTUAL
 					 : LOOKUP_NORMAL),
 					/*fn_p=*/NULL,
-					complain);
+					complain, loc);
 	}
     }
 
@@ -2167,7 +2167,7 @@ finish_call_expr (tree fn, VEC(tree,gc)
 				       ? LOOKUP_NORMAL|LOOKUP_NONVIRTUAL
 				       : LOOKUP_NORMAL),
 				      /*fn_p=*/NULL,
-				      complain);
+				      complain, loc);
     }
   else if (is_overloaded_fn (fn))
     {
@@ -3211,6 +3211,8 @@ finish_id_expression (tree id_expression
 	      decl = finish_non_static_data_member
 		       (decl, NULL_TREE,
 			/*qualifying_scope=*/NULL_TREE);
+	      if (EXPR_P (decl))
+		SET_EXPR_LOCATION (decl, location);
 	      pop_deferring_access_checks ();
 	      return decl;
 	    }
@@ -3288,6 +3290,8 @@ finish_id_expression (tree id_expression
 	  push_deferring_access_checks (dk_no_check);
 	  decl = finish_non_static_data_member (decl, NULL_TREE,
 						/*qualifying_scope=*/NULL_TREE);
+	  if (EXPR_P (decl))
+	    SET_EXPR_LOCATION (decl, location);
 	  pop_deferring_access_checks ();
 	}
       else if (is_overloaded_fn (decl))
@@ -4978,7 +4982,8 @@ finish_omp_barrier (void)
 {
   tree fn = builtin_decl_explicit (BUILT_IN_GOMP_BARRIER);
   VEC(tree,gc) *vec = make_tree_vector ();
-  tree stmt = finish_call_expr (fn, &vec, false, false, tf_warning_or_error);
+  tree stmt = finish_call_expr (fn, &vec, false, false, tf_warning_or_error,
+				input_location);
   release_tree_vector (vec);
   finish_expr_stmt (stmt);
 }
@@ -4988,7 +4993,8 @@ finish_omp_flush (void)
 {
   tree fn = builtin_decl_explicit (BUILT_IN_SYNC_SYNCHRONIZE);
   VEC(tree,gc) *vec = make_tree_vector ();
-  tree stmt = finish_call_expr (fn, &vec, false, false, tf_warning_or_error);
+  tree stmt = finish_call_expr (fn, &vec, false, false, tf_warning_or_error,
+				input_location);
   release_tree_vector (vec);
   finish_expr_stmt (stmt);
 }
@@ -4998,7 +5004,8 @@ finish_omp_taskwait (void)
 {
   tree fn = builtin_decl_explicit (BUILT_IN_GOMP_TASKWAIT);
   VEC(tree,gc) *vec = make_tree_vector ();
-  tree stmt = finish_call_expr (fn, &vec, false, false, tf_warning_or_error);
+  tree stmt = finish_call_expr (fn, &vec, false, false, tf_warning_or_error,
+				input_location);
   release_tree_vector (vec);
   finish_expr_stmt (stmt);
 }
@@ -5008,7 +5015,8 @@ finish_omp_taskyield (void)
 {
   tree fn = builtin_decl_explicit (BUILT_IN_GOMP_TASKYIELD);
   VEC(tree,gc) *vec = make_tree_vector ();
-  tree stmt = finish_call_expr (fn, &vec, false, false, tf_warning_or_error);
+  tree stmt = finish_call_expr (fn, &vec, false, false, tf_warning_or_error,
+				input_location);
   release_tree_vector (vec);
   finish_expr_stmt (stmt);
 }
Index: testsuite/g++.dg/warn/pr26785.C
===================================================================
--- testsuite/g++.dg/warn/pr26785.C	(revision 190939)
+++ testsuite/g++.dg/warn/pr26785.C	(working copy)
@@ -3,7 +3,7 @@
 // { dg-options "-fshow-column" }
 
 class foo {
-  foo::foo // { dg-error "3:extra qualification" }
+  foo::foo // { dg-error "8:extra qualification" }
   (int a, 
    int b,
    int c);
Index: testsuite/g++.dg/tc1/dr52.C
===================================================================
--- testsuite/g++.dg/tc1/dr52.C	(revision 190939)
+++ testsuite/g++.dg/tc1/dr52.C	(working copy)
@@ -16,8 +16,8 @@ private:
 struct B1 : B {};
 struct B2 : B {};
 
-struct C
-{ // { dg-error "C" }
+struct C  // { dg-error "C" }
+{
   void foo(void);
 };
 
Index: testsuite/g++.old-deja/g++.brendan/crash16.C
===================================================================
--- testsuite/g++.old-deja/g++.brendan/crash16.C	(revision 190939)
+++ testsuite/g++.old-deja/g++.brendan/crash16.C	(working copy)
@@ -8,7 +8,7 @@ public:
       Graph(void) {} // { dg-error "7:'Graph" }
 }
 
-Graph::Graph(void) // { dg-error "18:return type|1: error: redefinition" }
+Graph::Graph(void) // { dg-error "18:return type|8: error: redefinition" }
 {    N = 10;
 }

Comments

Ian Lance Taylor Sept. 18, 2012, 5:49 p.m. UTC | #1
On Tue, Sep 18, 2012 at 3:58 AM, Arnaud Charlet <charlet@adacore.com> wrote:
>
> Since this issue is more general, I have split my changes and introduced a new
> tentative switch called -fextra-slocs, which is the subject of this email.

Sorry for picking on simple stuff, but the switch name seems
meaningless, and there isn't any documentation.

Conceptually it looks like you are trying to make up for the absence
of a proper AST by building an on-the-side hash table to track
expression locations.  The hash table key is the tree structure
itself.  The thing is, any call into fold-const may give you an
entirely new tree, and at that point you have lost your extra location
information.  And the C/C++ frontends call into fold-const regularly,
which is why we don't have a proper AST in the first place.  So it
seems to me that this is going to be kind of frustrating, in that we
will often have the extra location information but sometimes we won't.
 And whether we have it or not will change as the frontends change.

So while a proper AST would be nice, I'm not convinced that this is
the right workaround.  Another approach might be to tie this to the
location information, because the location information does generally
survive fold-const.  E.g., perhaps we could grab a bit in the location
information to mean that it is special.  And we could keep an
on-the-side hash table mapping special location values to additional
location information.

Ian
Joseph Myers Sept. 18, 2012, 8:18 p.m. UTC | #2
On Tue, 18 Sep 2012, Ian Lance Taylor wrote:

> On Tue, Sep 18, 2012 at 3:58 AM, Arnaud Charlet <charlet@adacore.com> wrote:
> >
> > Since this issue is more general, I have split my changes and introduced a new
> > tentative switch called -fextra-slocs, which is the subject of this email.
> 
> Sorry for picking on simple stuff, but the switch name seems
> meaningless, and there isn't any documentation.

My simple points are that size_t is better than int for when you want to 
store the length of a list on the host, and that when building up a list 
of locations for an expression list it would be better to use a VEC rather 
than a fixed-length array.

> Another approach might be to tie this to the
> location information, because the location information does generally
> survive fold-const.  E.g., perhaps we could grab a bit in the location
> information to mean that it is special.  And we could keep an
> on-the-side hash table mapping special location values to additional
> location information.

I also think this is to be preferred.  I think of location_t as being, for 
most of the compiler, an opaque handle to location information.  Just as 
it can now represent information about different concepts of location in 
the presence of macro expansion, so it's entirely reasonable for a 
location_t value to represent information about a range of locations 
(start and end points) for an expression, or to represent information 
about the locations (or ranges of locations) of the operands and operators 
of an expression (in the absence of an unfolded AST, where for locations 
of operands themselves you could just look one level down in the AST).

I would guess that much of the patch would be unchanged with such an 
approach - you still need to pass extra location information to various 
places, but the details of how you attach it to the expressions might be 
different.

I would say that a location_t mapping to a set of other locations more 
complicated than at present should have some structure to how it maps to 
them.  That is, rather than just mapping to an array of values with those 
values used in different ways for different source code constructs, it 
should be possible to tell that a given location_t is mapping to certain 
locations as corresponding to first and second operands of an binary 
operator (for example).
Arnaud Charlet Sept. 19, 2012, 7:18 a.m. UTC | #3
Thanks for your feedback.

> I also think this is to be preferred.  I think of location_t as being, for
> most of the compiler, an opaque handle to location information.  Just as
> it can now represent information about different concepts of location in
> the presence of macro expansion, so it's entirely reasonable for a
> location_t value to represent information about a range of locations
> (start and end points) for an expression, or to represent information
> about the locations (or ranges of locations) of the operands and operators
> of an expression (in the absence of an unfolded AST, where for locations
> of operands themselves you could just look one level down in the AST).

OK, I agree that's an interesting/promising approach (using/reserving a bit
to add a level of indirection into a separate hash table), I'll investigate
what it could take to implement such approach, which would indeed get rid
of the duplicate_expr_locations() calls, since this would be implicit as
part of protected_set_expr_location() somehow, right?

> I would guess that much of the patch would be unchanged with such an
> approach - you still need to pass extra location information to various
> places, but the details of how you attach it to the expressions might be
> different.

Right, hopefully the main difference would be the implementation of
the new functions in tree.[ch] (possibly moved elsewhere?) to set/retrieve
these extra slocs, most of the changes would remain almost identical I
suspect.

> I would say that a location_t mapping to a set of other locations more
> complicated than at present should have some structure to how it maps to
> them.  That is, rather than just mapping to an array of values with those
> values used in different ways for different source code constructs, it
> should be possible to tell that a given location_t is mapping to certain
> locations as corresponding to first and second operands of an binary
> operator (for example).

OK, so you mean, instead of knowing the number of locations from the
tree kind (e.g. 1 extra sloc for unary exprs, 2 for binary exprs, ...),
we would encode this as part of the extra loc info? Note that the number of
extra locations is already stored in my current patch, this is the
unsigned char len field of sloc_Struct in tree.c.

Is that sufficient (knowing the number of slocs), or do you have something
more advanced in mind? Note that the structure stored in the hash table is:

typedef struct {
  const_tree node;
  unsigned char len;
  location_t locus[1];
} sloc_struct;

In other words, we have all the info you mentioned: number of extra slocs
(len field) and tree kind (via the node field).

If this is not what you had in mind, could you clarify what else?

Arno
Jakub Jelinek Sept. 19, 2012, 7:31 a.m. UTC | #4
On Wed, Sep 19, 2012 at 09:18:39AM +0200, Arnaud Charlet wrote:
> Thanks for your feedback.
> 
> > I also think this is to be preferred.  I think of location_t as being, for
> > most of the compiler, an opaque handle to location information.  Just as
> > it can now represent information about different concepts of location in
> > the presence of macro expansion, so it's entirely reasonable for a
> > location_t value to represent information about a range of locations
> > (start and end points) for an expression, or to represent information
> > about the locations (or ranges of locations) of the operands and operators
> > of an expression (in the absence of an unfolded AST, where for locations
> > of operands themselves you could just look one level down in the AST).
> 
> OK, I agree that's an interesting/promising approach (using/reserving a bit
> to add a level of indirection into a separate hash table), I'll investigate
> what it could take to implement such approach, which would indeed get rid
> of the duplicate_expr_locations() calls, since this would be implicit as
> part of protected_set_expr_location() somehow, right?

Please also read Dodji's slides from Cauldron on this:
http://dodji.seketeli.net/talks/gnu-cauldron-2012/track-macro-locations.pdf
and discuss with him, there is also the
"Combine location with block using block_locations" patchset
floating around that interferes with this partially.  But range locations as
part of location_t is definitely the way to go, we want that for proper
diagnostics anyway.

	Jakub
Joseph Myers Sept. 19, 2012, 3:04 p.m. UTC | #5
On Wed, 19 Sep 2012, Arnaud Charlet wrote:

> OK, so you mean, instead of knowing the number of locations from the
> tree kind (e.g. 1 extra sloc for unary exprs, 2 for binary exprs, ...),
> we would encode this as part of the extra loc info? Note that the number of

No, I mean that rather than the front end using an interface defined as 
"set the first location to A and the second location to B" it should use 
one meaning "set the first operand's location to A and the second 
operand's location to B" or "set the beginning of the range to A and the 
end of the range to B" - the extra locations should in some way be tagged 
to indicate what their relation is to the expression, and users of the 
locations should then also look things up by tag rather than knowing that 
internally the first location for a binary operation has some particular 
semantics.

As illustrated in the example I gave, there's more than one way one might 
naturally associate two locations with a binary operation, hence the 
desire for code to operate on them at a better-defined level than "first" 
and "second" locations.  If you then want to insert a third location in 
the middle (the location of the operator, say) then you don't need to 
change everything that referred to locations symbolically.
Manuel López-Ibáñez Nov. 1, 2012, 11:59 p.m. UTC | #6
On 18 September 2012 12:58, Arnaud Charlet <charlet@adacore.com> wrote:
> This is a relatively large patch which tackles the difficult issue of
> generating more sloc information in front-ends (in particular C and C++
> front-ends), so that "static analyzers" based on GCC can get more useful
> and detailed information.
>
> This happens to be related/similar to the need mentioned in PR43486, hence
> the mention of this PR in the subject line.
>
> In my work, as I mentioned a few times in the past, this patch is the
> first foundation for another patch, which will provide source navigation
> capabilities in IDEs/editors, by generating source cross reference information
> based on the C and C++ front-ends (introducing a new switch -fdump-xref).
> Ideally I'd like to submit both patches.
>
> Since this issue is more general, I have split my changes and introduced a new
> tentative switch called -fextra-slocs, which is the subject of this email.
>
> In order to provide more sloc info attached to nodes (and in particular
> VAR_DECLs), there are basically two possibilities:
>
> - the one mentioned in comment 4 of PR43486:
>   << Jason Merrill 2010-03-23 01:51:34 UTC
>
>   I suppose we could wrap rvalue uses in NOP_EXPR and lvalue uses in
>   VIEW_CONVERT_EXPR.
>   >>
>
>   I investigated this option, and unfortunately this does not work
>   because of folding occurring all over the place in the C and
>   C++ front-ends, removing these extra EXPRs very early.

These are issues that require to be fixed. The front-ends should only
fold when actually required, and then, they should keep around the
original expression.

>   So I opted for the second approach:
> - Keep another data structure which will associate extra slocs with some
>   node expressions. The idea is that since you can't associate slocs with
>   VAR_DECLs in an expr, instead we store extra slocs in the containing
>   expression node. For instance, all binary expressions (add ADD, MINUS, ...)
>   will contain two extra slocs: one for the left hand side, and one for the
>   right hand side of the expression. One extra sloc for unary operators,
>   etc...

Consider bugs like: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=55173,
where a warning ocurs in a system-header, but GCC does not see this
because the expression that triggers that warning does not have a
location, so input_location is used, which is not at the header.  The
proposed approach does not fully solve such bugs, because  at the
point of warning, only the operands are available, and not the
containing expression node, so the additional locations are not
available. Thus, this second approach would still require passing an
explicit location all over the FE. Admitedly, this is what we are
currently doing in many cases. But do we want to add one extra (or
worse, one per tree argument) explicit location argument on almost
every FE function? For functions that accept tree nodes with location
and without location, this is a waste, because depending on the
particular expression, we will pass two times the same location
(explicitly and implicitly as part of the node). Even worse, many
functions that don't care about locations will have to pass around
locations.

I think, in the long-term, your proposed approach is less useful than
fixing early folding and adding locations (via a wrapper or an
on-the-side index) to the tree nodes that don't have one. But, since
nobody is working on the latter, the "long term" may be longer than
the time until someone makes a LLVM-to-GIMPLE conversor, and replaces
the C/C++ FEs by clang. ;-)  So, as a short-term fix, your proposed
approach is definitely better than the status-quo, so I hope you
manage to get it finished.

Cheers,

Manuel.
Arnaud Charlet Nov. 4, 2012, 5:15 p.m. UTC | #7
> I think, in the long-term, your proposed approach is less useful than
> fixing early folding and adding locations (via a wrapper or an
> on-the-side index) to the tree nodes that don't have one. But, since
> nobody is working on the latter, the "long term" may be longer than
> the time until someone makes a LLVM-to-GIMPLE conversor, and replaces
> the C/C++ FEs by clang. ;-)

Given the current state of front end trees and LLVM, I suspect indeed most people interested in e.g. static analysis will likely switch to LLVM if not already done.

>  So, as a short-term fix, your proposed
> approach is definitely better than the status-quo, so I hope you
> manage to get it finished.

Unfortunately the suggestions I received, while they make sense on paper require a large amount of work, which I can hardly justify for now and certainly not in the short term. I suspect it would take me less time to redo my work using LLVM than to introduce a whole new API/kludge on top of gcc locations, with also still a large unknown as to whether my new patch would be accepted without yet another large rewrite.

Or to put it another way, it does not really make much more sense to me to use pseudo locations than my current htable approach (it only marginally improves things, for a large development effort), in particular since a better solution would be to have a real C and C++ AST, which I suspect GCC will end up having sooner or later (e.g. via clang as you hinted).

Arno
diff mbox

Patch

Index: common.opt
===================================================================
--- common.opt	(revision 190939)
+++ common.opt	(working copy)
@@ -1098,6 +1098,10 @@  fexcess-precision=
 Common Joined RejectNegative Enum(excess_precision) Var(flag_excess_precision_cmdline) Init(EXCESS_PRECISION_DEFAULT)
 -fexcess-precision=[fast|standard]	Specify handling of excess floating-point precision
 
+fextra-slocs
+Common Var(flag_extra_slocs) Init(0)
+Generate extra sloc information on expression trees
+
 Enum
 Name(excess_precision) Type(enum excess_precision) UnknownError(unknown excess precision style %qs)
 
Index: tree.c
===================================================================
--- tree.c	(revision 190939)
+++ tree.c	(working copy)
@@ -3561,6 +3561,8 @@  stabilize_reference (tree ref)
   TREE_READONLY (result) = TREE_READONLY (ref);
   TREE_SIDE_EFFECTS (result) = TREE_SIDE_EFFECTS (ref);
   TREE_THIS_VOLATILE (result) = TREE_THIS_VOLATILE (ref);
+  protected_set_expr_location (result, EXPR_LOCATION (ref));
+  duplicate_expr_locations (result, ref);
 
   return result;
 }
@@ -4157,6 +4159,123 @@  build_block (tree vars, tree subblocks,
   return block;
 }
 
+/* extra location hash table */
+
+typedef struct {
+  const_tree node;
+  unsigned char len;
+  location_t locus[1];
+} sloc_struct;
+
+static htab_t extra_slocs = NULL;
+
+/* Hash function used by extra_slocs htable.  */
+static hashval_t
+node_hash (const void *p)
+{
+  const sloc_struct *m = (const sloc_struct *)p;
+  return htab_hash_pointer (m->node);
+}
+
+/* Comparison function used by extra_slocs htable.  */
+static int
+node_eq (const void *p, const void *q)
+{
+  const sloc_struct *a = (const sloc_struct *)p;
+  const sloc_struct *b = (const sloc_struct *)q;
+
+  return a->node == b->node;
+}
+
+/* Only relevant for EXPR_P nodes and when flag_extra_slocs is enabled.
+   Return secondary locations if any for a given expression. This is useful for
+   getting precise sloc information in particular correct column
+   numbers for each member of an expression. Unary expressions will return one
+   extra location, binary expressions will return two, COND_EXPR 3 extra,
+   etc... CALL_EXPR will return one extra loc for the precise location of the
+   function or method called, and one extra loc per argument.  */
+location_t *
+expr_locations (const_tree node)
+{
+  sloc_struct key;
+  sloc_struct *res;
+
+  if (!extra_slocs)
+    return NULL;
+
+  key.node = node;
+  res = (sloc_struct *) htab_find (extra_slocs, &key);
+  return res ? res->locus : NULL;
+}
+
+/* Return the nth location N associated with NODE, or UNKNOWN_LOCATION if
+   no extra location can be found (or -fextra-slocs is not set).  */
+location_t
+expr_location_n (const_tree node, int n)
+{
+  sloc_struct key;
+  sloc_struct *res;
+
+  if (!extra_slocs)
+    return UNKNOWN_LOCATION;
+
+  key.node = node;
+  res = (sloc_struct *) htab_find (extra_slocs, &key);
+
+  if (res && res->len > n)
+    return res->locus[n];
+  else
+    return UNKNOWN_LOCATION;
+}
+
+/* Set extra locations associated with NODE. LOCUS is an array of LEN
+   locations.  No-op unless -fextra-slocs is set.  */
+void
+set_expr_locations (tree node, location_t *locus, int len)
+{
+  sloc_struct *m;
+
+  if (!flag_extra_slocs)
+    return;
+
+  if (!extra_slocs)
+    extra_slocs = htab_create (8192, node_hash, node_eq, free);
+
+  m = (sloc_struct *)
+       xcalloc (1, sizeof (sloc_struct) + sizeof (location_t) * (len - 1));
+  m->node = node;
+  m->len = len;
+  memcpy (m->locus, locus, sizeof (location_t) * len);
+  *htab_find_slot (extra_slocs, m, INSERT) = m;
+}
+
+/* Set an extra location LOCUS for tree NODE.  No-op unless -fextra-slocs.  */
+void
+set_expr_location2 (tree node, location_t locus)
+{
+  if (!flag_extra_slocs)
+    return;
+
+  location_t l = locus;
+  set_expr_locations (node, &l, 1);
+}
+
+/* Copy extra locations associated with TARGET to SOURCE, if any.  */
+void
+duplicate_expr_locations (tree target, tree source)
+{
+  sloc_struct key;
+  sloc_struct *res;
+
+  if (!extra_slocs || !CAN_HAVE_LOCATION_P (target))
+    return;
+
+  key.node = source;
+  res = (sloc_struct *) htab_find (extra_slocs, &key);
+
+  if (res)
+    set_expr_locations (target, res->locus, res->len);
+}
 
 /* Like SET_EXPR_LOCATION, but make sure the tree can have a location.
 
Index: tree.h
===================================================================
--- tree.h	(revision 190939)
+++ tree.h	(working copy)
@@ -1618,7 +1618,10 @@  struct GTY(()) tree_constructor {
    return nothing.  */
 #define EXPR_LOCATION(NODE) \
   (CAN_HAVE_LOCATION_P ((NODE)) ? (NODE)->exp.locus : UNKNOWN_LOCATION)
+#define EXPR_LOCATIONS(NODE) expr_locations ((NODE))
 #define SET_EXPR_LOCATION(NODE, LOCUS) EXPR_CHECK ((NODE))->exp.locus = (LOCUS)
+#define SET_EXPR_LOCATION2(NODE, FROM) set_expr_location2 ((NODE), (FROM))
+#define SET_EXPR_LOCATIONS(NODE, FROM, LENGTH) set_expr_locations ((NODE), (FROM), (LENGTH))
 #define EXPR_HAS_LOCATION(NODE) (EXPR_LOCATION (NODE) != UNKNOWN_LOCATION)
 /* The location to be used in a diagnostic about this expression.  Do not
    use this macro if the location will be assigned to other expressions.  */
@@ -1631,6 +1634,11 @@  struct GTY(()) tree_constructor {
 #define CAN_HAVE_LOCATION_P(NODE) ((NODE) && EXPR_P (NODE))
 
 extern void protected_set_expr_location (tree, location_t);
+extern location_t *expr_locations (const_tree);
+extern location_t expr_location_n (const_tree, int);
+extern void set_expr_location2 (tree, location_t);
+extern void set_expr_locations (tree, location_t *, int);
+extern void duplicate_expr_locations (tree, tree);
 
 /* In a TARGET_EXPR node.  */
 #define TARGET_EXPR_SLOT(NODE) TREE_OPERAND_CHECK_CODE (NODE, TARGET_EXPR, 0)