From patchwork Sat Nov 7 03:58:57 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 541212 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id C19F21409A0 for ; Sat, 7 Nov 2015 14:41:11 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b=V6g8Wg20; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:mime-version:content-type :content-transfer-encoding; q=dns; s=default; b=bieAWjkTwND9/c4x x+FijGYLQzfO8zS3pBkUiVZ6OUg5aiY8QOjxfvsb/YI0aAXtt2P6M3x15cycVOgA h5B6dJMFJxJVJSr0oxzho3IT3AiN92Agr1WUEUIo6ak87UH1fUmbIGKbrBIxIF7M fFFWPf+ZvsjwYz46YTIYpodCcSs= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:mime-version:content-type :content-transfer-encoding; s=default; bh=dUeujOFTAfKNceANLtNebq 8NNHc=; b=V6g8Wg20WokGf43i7WUvaEp35k1HKf+f9XGIrn+eL9lnT6tg/GoEnV FqnlAglWhfU6wPNTLdeJteAyTecbaCih2azru4HeWVwM1vLeWsn8tLtqRIJpBE3R AJeWfr07FX0COaVk7LSJpj+/dr+QtO10oqaxZNIhpzNoia89A7Jkc= Received: (qmail 25466 invoked by alias); 7 Nov 2015 03:40:58 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 25451 invoked by uid 89); 7 Nov 2015 03:40:57 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-0.1 required=5.0 tests=AWL, BAYES_50, KAM_ASCII_DIVIDERS, SPF_HELO_PASS, T_RP_MATCHES_RCVD autolearn=no version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Sat, 07 Nov 2015 03:40:49 +0000 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (Postfix) with ESMTPS id 7F8858E234 for ; Sat, 7 Nov 2015 03:40:48 +0000 (UTC) Received: from c64.redhat.com (vpn-231-202.phx2.redhat.com [10.3.231.202]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id tA73elEH005776; Fri, 6 Nov 2015 22:40:47 -0500 From: David Malcolm To: gcc-patches@gcc.gnu.org Cc: David Malcolm Subject: [PATCH/RFC] C++ FE: expression ranges (work in progress) Date: Fri, 6 Nov 2015 22:58:57 -0500 Message-Id: <1446868737-3306-1-git-send-email-dmalcolm@redhat.com> MIME-Version: 1.0 X-IsSubscribed: yes Caveat: this patch is a work-in-progress, but I thought it was worth posting to check that the concept is OK. This patch builds on top of the patch kit: "[PATCH 00/10] Overhaul of diagnostics (v5)" https://gcc.gnu.org/ml/gcc-patches/2015-10/msg02536.html of which patches 1-4 are now in trunk. Note that the above kit generalizes the meaning of location_t to mean a combination of both a caret location *and* a source range. This patch is analogous to: "[PATCH 06/10] Track expression ranges in C frontend" https://gcc.gnu.org/ml/gcc-patches/2015-10/msg02535.html in that it adds range information to the various expressions, but this time for the C++ frontend. The benefit here would be e.g. to highlight the LHS and RHS of a bad binary expression: error: no match for ‘operator+’ (operand types are ‘int’ and ‘s’) return (first_function_with_a_very_long_name () ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + second_function_with_a_very_long_name ()); ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ where exactly which is the LHS and RHS can be unclear when dealing with complicated nesting of expressions. As with the C frontend, there's an issue with tree nodes that don't have locations: VAR_DECL, INTEGER_CST, etc: int test (int foo) { return foo * 100; ^^^ ^^^ } where we'd like to access the source spelling ranges of the expressions during parsing, so that we can use them when reporting parser errors. I resolved this for the C frontend by augmenting struct c_expr. However, the C++ parser works purely on "tree". Hence this patch introduces a new class "cp_expr", which bundles a tree and a location_t, with implicit conversion between tree and cp_expr via a ctor, an operator tree () etc, effectively preserving location information for things like VAR_DECL during parsing. (I first attempted explicit conversions, but it turned out to be a huge task; doing it implicitly made it doable). Some explicit "expr.get_value ()" method calls are needed for variadic calls e.g.: error_at (id_expr_token->location, "local variable %qD may not appear in this context", - decl); + decl.get_value ()); since operator tree () can't be used there. For tree nodes with a location, the location_t is always the same as the EXPR_LOCATION of node (and redundant). For other tree nodes, the location_t is of interest (and is asserted to not be UNKNOWN_LOCATION). Most of the time, the cp_expr's location is rather redundant, but it's essential at the leaves of the parse tree, where it's used for preserving the locations of the tokens as they become identifiers, constants, etc: an extra 32-bit value is being stored and passed around with the tree ptrs. The new tests were shameless copied from the C FE testsuite, fixing them up to reflect some apparent differences in caret location between C and C++ for some expression, and adding some C++-specific constructs (perhaps it could be done by referencing the C tests in g++.dg/plugin/plugin.exp, with relative paths, rather than copying them?) This isn't ready yet: * it has FIXMEs * although it seems to bootstrap, it has about 100 regressions in g++.sum, and a lot of regressions in obj-c++.sum * I haven't done performance testing yet That said, is this the right direction? Also, in cp_parser_new_expression I attempted to generate meaningful ranges e.g.: int *foo = new int[100]; ^~~~~~~~~~~~ but it seems to be hard to do this, due to the trailing optional components; I found myself wanting to ask the lexer for the last token it consumed (in particular, I'm interested in the location_t of that token). Is this a reasonable thing to add to the lexer? Thanks gcc/ChangeLog: * convert.c (convert_to_integer): In integer-to-integer conversions, preserve the location. gcc/cp/ChangeLog: * call.c (build_new_op_1): Set the location of the result of cp_build_unary_op. * cp-tree.h (class cp_expr): New class. (perform_koenig_lookup): Convert return type and param from tree to cp_expr. (finish_increment_expr): Likewise. (finish_unary_op_expr): Likewise. (finish_id_expression): Likewise for return type. (build_class_member_access_expr): Likewise for param. (finish_class_member_access_expr): Likewise. (build_x_unary_op): Likewise. (build_c_cast): Likewise. (build_x_modify_expr): Likewise for return type. * name-lookup.c (lookup_arg_dependent_1): Likewise. (lookup_arg_dependent): Likewise; also for local "ret". * name-lookup.h (lookup_arg_dependent): Likewise for return type. * parser.c (struct cp_parser_expression_stack_entry): Likewise for field "lhs". (cp_parser_identifier): Likewise for return type. Use cp_expr ctor to preserve the token's location. (cp_parser_string_literal): Likewise, building up a meaningful location for the case where a compound string literal is built by concatentation. (cp_parser_userdef_char_literal): Likewise for return type. (cp_parser_userdef_numeric_literal): Likewise. (cp_parser_fold_expression): Likewise. (cp_parser_primary_expression): Likewise, and for locals "expr", "lam", "id_expression", "decl". Use cp_expr ctor when parsing literals, to preserve the spelling location of the token. Preserve the locations of parentheses. FIXME: disable call to objc_lookup_ivar. (cp_parser_primary_expression): Convert return type from tree to cp_expr. (cp_parser_id_expression): Likewise. (cp_parser_unqualified_id): Likewise. Also for local "id". (cp_parser_postfix_expression): Likewise, also for local "postfix_expression". Preserve start location. Use it to construct spelling ranges for C++-style casts. Pass on the location of the closing parenthesis of a call site to cp_parser_parenthesized_expression_list, and use it to build a source range for a call. Use cp_expr in ternary expression. (cp_parser_postfix_dot_deref_expression): Convert param from tree to cp_expr. (cp_parser_parenthesized_expression_list): Add "close_paren_loc" out-param, and write back to it. (cp_parser_unary_expression): Convert return type from tree to cp_expr. Also for locals "cast_expression" and "expression". Generate suitable locations for cast expressions. Call protected_set_expr_location on expression. (cp_parser_new_expression): FIXME: unfinished attempt to generate meaningful locations (see note in blurb above). (cp_parser_cast_expression): Convert return type from tree to cp_expr; also for local "expr". Use the paren location to generate a meaningful range for the expression. (cp_parser_binary_expression): Convert return type from tree to cp_expr; also for local "rhs". Generate a meaningful location for the expression, and use it. Replace call to protected_set_expr_location by converting a build2 to a build2_loc. (cp_parser_question_colon_clause): Convert return type from tree to cp_expr; also for local "assignmend_expr". Set the spelling range of the expression. (cp_parser_assignment_expression): Likewise for return type and locals "expr" and "rhs". Build a meaningful spelling range for the expression, and call protected_set_expr_location since build_x_modify_expr appears not to always honor "loc". (cp_parser_expression): Likewise for return type and locals "expression" and "assignment_expression". Build a meaningful spelling range for assignment expressions. (cp_parser_constant_expression): Likewise for return type and local "expression". (cp_parser_lambda_expression): Likewise for return type. (cp_parser_operator_function_id): Likewise. (cp_parser_operator): Likewise. Generate a meaningful range, using cp_expr's ctor to return it. (cp_parser_initializer_clause): Likewise for local "initializer". (cp_parser_lookup_name): Likewise for return type. Use cp_expr's ctor to preserve the location_t of the name. (cp_parser_simple_cast_expression): Likewise for return type. * semantics.c (perform_koenig_lookup): Likewise for return type and param. (finish_increment_expr): Likewise. Generate a meaningful spelling range. (finish_unary_op_expr): Likewise. (finish_id_expression): Likewise. * typeck.c (build_class_member_access_expr): Likewise for param; generate range for result. (finish_class_member_access_expr): Likewise. (cp_build_binary_op): Convert a build2 to a build2_loc. (build_x_unary_op): Convert param from tree to cp_expr. (build_nop): Use the location of the input expression for the NOP_EXPR. (build_c_cast): Use the provided location_t for the result. Provide an overloaded variant that takes a cp_esxpr and returns a cp_expr. (build_x_modify_expr): Convert return type from tree to cp_expr. Call protected_set_expr_location on the result of cp_build_modify_expr. * typeck2.c (build_x_arrow): Call protected_set_expr_location on the result of cp_build_indirect_ref. gcc/testsuite/ChangeLog: * g++.dg/plugin/diagnostic-test-expressions-1.c: New file. * g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c: New file. * g++.dg/plugin/plugin.exp (plugin_test_list): Add the above. gcc/ChangeLog: * tree.c (get_pure_location): Make non-static. (set_source_range): Return the resulting location_t. (make_location): New function. * tree.h (get_pure_location): New decl. (set_source_range): Convert return type from void to location_t. (make_location): New decl. --- gcc/convert.c | 2 +- gcc/cp/call.c | 6 +- gcc/cp/cp-tree.h | 98 +++- gcc/cp/name-lookup.c | 6 +- gcc/cp/name-lookup.h | 2 +- gcc/cp/parser.c | 299 ++++++++---- gcc/cp/semantics.c | 29 +- gcc/cp/typeck.c | 60 ++- gcc/cp/typeck2.c | 4 +- .../g++.dg/plugin/diagnostic-test-expressions-1.c | 535 +++++++++++++++++++++ .../diagnostic_plugin_test_tree_expression_range.c | 98 ++++ gcc/testsuite/g++.dg/plugin/plugin.exp | 5 +- gcc/tree.c | 25 +- gcc/tree.h | 10 +- 14 files changed, 1026 insertions(+), 153 deletions(-) create mode 100644 gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.c create mode 100644 gcc/testsuite/g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c diff --git a/gcc/convert.c b/gcc/convert.c index bff2978..f17a328 100644 --- a/gcc/convert.c +++ b/gcc/convert.c @@ -612,7 +612,7 @@ convert_to_integer (tree type, tree expr) else code = NOP_EXPR; - return fold_build1 (code, type, expr); + return fold_build1_loc (loc, code, type, expr); } /* If TYPE is an enumeral type or a type with a precision less diff --git a/gcc/cp/call.c b/gcc/cp/call.c index f8db2df..e377a87 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -5762,7 +5762,11 @@ build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1, case REALPART_EXPR: case IMAGPART_EXPR: case ABS_EXPR: - return cp_build_unary_op (code, arg1, candidates != 0, complain); + { + tree result = cp_build_unary_op (code, arg1, candidates != 0, complain); + protected_set_expr_location (result, loc); + return result; + } case ARRAY_REF: return cp_build_array_ref (input_location, arg1, arg2, complain); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index f650c76..6a75ce9 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -40,6 +40,86 @@ c-common.h, not after. #include "c-family/c-common.h" #include "diagnostic.h" +/* A tree node, together with a location, so that we can track locations + (and ranges) during parsing. + + The location is redundant for node kinds that have locations, + but not all node kinds do (e.g. constants, and references to + params, locals, etc), so we stash a copy here. */ + +class cp_expr +{ +public: + cp_expr () : + m_value (NULL), m_loc (UNKNOWN_LOCATION) {} + + cp_expr (tree value) : + m_value (value), m_loc (EXPR_LOCATION (m_value)) + { +#if 0 + /* FIXME: various assertions can be put in here when debugging, + for tracking down where location information gets thrown + away (during a trip through a purely "tree" value). */ + if (m_value && m_value != error_mark_node) + { + if (TREE_CODE (m_value) == FUNCTION_DECL) + return; // for now + gcc_assert (CAN_HAVE_LOCATION_P (m_value)); + //gcc_assert (m_loc != UNKNOWN_LOCATION); + } +#endif + } + + cp_expr (tree value, location_t loc): + m_value (value), m_loc (loc) + { + if (m_value) + gcc_assert (m_loc != UNKNOWN_LOCATION); + } + + cp_expr (const cp_expr &other) : + m_value (other.m_value), m_loc (other.m_loc) {} + + /* Implicit conversions to tree. */ + operator tree () const { return m_value; } + tree & operator* () { return m_value; } + tree & operator-> () { return m_value; } + + tree get_value () const { return m_value; } + location_t get_location () const { return m_loc; } + location_t get_start () const + { + source_range src_range = get_range_from_loc (line_table, m_loc); + return src_range.m_start; + } + location_t get_finish () const + { + source_range src_range = get_range_from_loc (line_table, m_loc); + return src_range.m_finish; + } + + void set_location (location_t loc) + { + protected_set_expr_location (m_value, loc); + m_loc = loc; + } + + void set_range (location_t start, location_t finish) + { + m_loc = set_source_range (m_value, start, finish); + } + + private: + tree m_value; + location_t m_loc; +}; + +inline bool +operator == (const cp_expr &lhs, tree rhs) +{ + return lhs.get_value () == rhs; +} + #include "name-lookup.h" /* Usage of TREE_LANG_FLAG_?: @@ -6250,15 +6330,15 @@ extern tree finish_stmt_expr_expr (tree, tree); extern tree finish_stmt_expr (tree, bool); extern tree stmt_expr_value_expr (tree); bool empty_expr_stmt_p (tree); -extern tree perform_koenig_lookup (tree, vec *, +extern cp_expr perform_koenig_lookup (cp_expr, vec *, tsubst_flags_t); extern tree finish_call_expr (tree, vec **, bool, bool, tsubst_flags_t); extern tree finish_template_variable (tree, tsubst_flags_t = tf_warning_or_error); -extern tree finish_increment_expr (tree, enum tree_code); +extern cp_expr finish_increment_expr (cp_expr, enum tree_code); extern tree finish_this_expr (void); extern tree finish_pseudo_destructor_expr (tree, tree, tree, location_t); -extern tree finish_unary_op_expr (location_t, enum tree_code, tree, +extern cp_expr finish_unary_op_expr (location_t, enum tree_code, cp_expr, tsubst_flags_t); extern tree finish_compound_literal (tree, tree, tsubst_flags_t); extern tree finish_fname (tree); @@ -6272,7 +6352,7 @@ extern tree finish_base_specifier (tree, tree, bool); extern void finish_member_declaration (tree); extern bool outer_automatic_var_p (tree); extern tree process_outer_var_ref (tree, tsubst_flags_t); -extern tree finish_id_expression (tree, tree, tree, +extern cp_expr finish_id_expression (tree, tree, tree, cp_id_kind *, bool, bool, bool *, bool, bool, bool, bool, @@ -6510,9 +6590,9 @@ extern tree unlowered_expr_type (const_tree); extern tree decay_conversion (tree, tsubst_flags_t, bool = true); -extern tree build_class_member_access_expr (tree, tree, tree, bool, +extern tree build_class_member_access_expr (cp_expr, tree, tree, bool, tsubst_flags_t); -extern tree finish_class_member_access_expr (tree, tree, bool, +extern tree finish_class_member_access_expr (cp_expr, tree, bool, tsubst_flags_t); extern tree build_x_indirect_ref (location_t, tree, ref_operator, tsubst_flags_t); @@ -6534,7 +6614,7 @@ extern tree build_x_binary_op (location_t, extern tree build_x_array_ref (location_t, tree, tree, tsubst_flags_t); extern tree build_x_unary_op (location_t, - enum tree_code, tree, + enum tree_code, cp_expr, tsubst_flags_t); extern tree cp_build_addr_expr (tree, tsubst_flags_t); extern tree cp_build_unary_op (enum tree_code, tree, int, @@ -6554,8 +6634,10 @@ extern tree build_static_cast (tree, tree, tsubst_flags_t); extern tree build_reinterpret_cast (tree, tree, tsubst_flags_t); extern tree build_const_cast (tree, tree, tsubst_flags_t); extern tree build_c_cast (location_t, tree, tree); +extern cp_expr build_c_cast (location_t loc, tree type, + cp_expr expr); extern tree cp_build_c_cast (tree, tree, tsubst_flags_t); -extern tree build_x_modify_expr (location_t, tree, +extern cp_expr build_x_modify_expr (location_t, tree, enum tree_code, tree, tsubst_flags_t); extern tree cp_build_modify_expr (tree, enum tree_code, tree, diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index b503012..35c27d5 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -5666,7 +5666,7 @@ arg_assoc (struct arg_lookup *k, tree n) /* Performs Koenig lookup depending on arguments, where fns are the functions found in normal lookup. */ -static tree +static cp_expr lookup_arg_dependent_1 (tree name, tree fns, vec *args) { struct arg_lookup k; @@ -5727,10 +5727,10 @@ lookup_arg_dependent_1 (tree name, tree fns, vec *args) /* Wrapper for lookup_arg_dependent_1. */ -tree +cp_expr lookup_arg_dependent (tree name, tree fns, vec *args) { - tree ret; + cp_expr ret; bool subtime; subtime = timevar_cond_start (TV_NAME_LOOKUP); ret = lookup_arg_dependent_1 (name, fns, args); diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h index d430edb..d2453e9 100644 --- a/gcc/cp/name-lookup.h +++ b/gcc/cp/name-lookup.h @@ -347,7 +347,7 @@ extern void do_toplevel_using_decl (tree, tree, tree); extern void do_local_using_decl (tree, tree, tree); extern tree do_class_using_decl (tree, tree); extern void do_using_directive (tree); -extern tree lookup_arg_dependent (tree, tree, vec *); +extern cp_expr lookup_arg_dependent (tree, tree, vec *); extern bool is_associated_namespace (tree, tree); extern void parse_using_directive (tree, tree); extern tree innermost_non_namespace_value (tree); diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index d4ef7f9..b83fe06 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -1784,7 +1784,7 @@ struct cp_parser_expression_stack_entry { /* Left hand side of the binary operation we are currently parsing. */ - tree lhs; + cp_expr lhs; /* Original tree code for left hand side, if it was a binary expression itself (used for -Wparentheses). */ enum tree_code lhs_type; @@ -1938,15 +1938,15 @@ static cp_parser *cp_parser_new /* Lexical conventions [gram.lex] */ -static tree cp_parser_identifier +static cp_expr cp_parser_identifier (cp_parser *); -static tree cp_parser_string_literal +static cp_expr cp_parser_string_literal (cp_parser *, bool, bool, bool); -static tree cp_parser_userdef_char_literal +static cp_expr cp_parser_userdef_char_literal (cp_parser *); static tree cp_parser_userdef_string_literal (tree); -static tree cp_parser_userdef_numeric_literal +static cp_expr cp_parser_userdef_numeric_literal (cp_parser *); /* Basic concepts [gram.basic] */ @@ -1956,11 +1956,11 @@ static bool cp_parser_translation_unit /* Expressions [gram.expr] */ -static tree cp_parser_primary_expression +static cp_expr cp_parser_primary_expression (cp_parser *, bool, bool, bool, cp_id_kind *); -static tree cp_parser_id_expression +static cp_expr cp_parser_id_expression (cp_parser *, bool, bool, bool *, bool, bool); -static tree cp_parser_unqualified_id +static cp_expr cp_parser_unqualified_id (cp_parser *, bool, bool, bool, bool); static tree cp_parser_nested_name_specifier_opt (cp_parser *, bool, bool, bool, bool); @@ -1968,19 +1968,19 @@ static tree cp_parser_nested_name_specifier (cp_parser *, bool, bool, bool, bool); static tree cp_parser_qualifying_entity (cp_parser *, bool, bool, bool, bool, bool); -static tree cp_parser_postfix_expression +static cp_expr cp_parser_postfix_expression (cp_parser *, bool, bool, bool, bool, cp_id_kind *); static tree cp_parser_postfix_open_square_expression (cp_parser *, tree, bool, bool); static tree cp_parser_postfix_dot_deref_expression - (cp_parser *, enum cpp_ttype, tree, bool, cp_id_kind *, location_t); + (cp_parser *, enum cpp_ttype, cp_expr, bool, cp_id_kind *, location_t); static vec *cp_parser_parenthesized_expression_list - (cp_parser *, int, bool, bool, bool *, bool = false); + (cp_parser *, int, bool, bool, bool *, bool = false, location_t * = NULL); /* Values for the second parameter of cp_parser_parenthesized_expression_list. */ enum { non_attr = 0, normal_attr = 1, id_attr = 2 }; static void cp_parser_pseudo_destructor_name (cp_parser *, tree, tree *, tree *); -static tree cp_parser_unary_expression +static cp_expr cp_parser_unary_expression (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false, bool = false); static enum tree_code cp_parser_unary_operator (cp_token *); @@ -1998,23 +1998,23 @@ static vec *cp_parser_new_initializer (cp_parser *); static tree cp_parser_delete_expression (cp_parser *); -static tree cp_parser_cast_expression +static cp_expr cp_parser_cast_expression (cp_parser *, bool, bool, bool, cp_id_kind *); -static tree cp_parser_binary_expression +static cp_expr 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); -static tree cp_parser_assignment_expression + (cp_parser *, cp_expr); +static cp_expr cp_parser_assignment_expression (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false); static enum tree_code cp_parser_assignment_operator_opt (cp_parser *); -static tree cp_parser_expression +static cp_expr cp_parser_expression (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false); -static tree cp_parser_constant_expression +static cp_expr cp_parser_constant_expression (cp_parser *, bool = false, bool * = NULL); static tree cp_parser_builtin_offsetof (cp_parser *); -static tree cp_parser_lambda_expression +static cp_expr cp_parser_lambda_expression (cp_parser *); static void cp_parser_lambda_introducer (cp_parser *, tree); @@ -2169,7 +2169,7 @@ static void cp_parser_function_body (cp_parser *, bool); static tree cp_parser_initializer (cp_parser *, bool *, bool *); -static tree cp_parser_initializer_clause +static cp_expr cp_parser_initializer_clause (cp_parser *, bool *); static tree cp_parser_braced_list (cp_parser*, bool*); @@ -2237,9 +2237,9 @@ static tree cp_parser_mem_initializer_id /* Overloading [gram.over] */ -static tree cp_parser_operator_function_id +static cp_expr cp_parser_operator_function_id (cp_parser *); -static tree cp_parser_operator +static cp_expr cp_parser_operator (cp_parser *); /* Templates [gram.temp] */ @@ -2410,7 +2410,7 @@ static tree cp_parser_objc_struct_declaration /* Utility Routines */ -static tree cp_parser_lookup_name +static cp_expr cp_parser_lookup_name (cp_parser *, tree, enum tag_types, bool, bool, bool, tree *, location_t); static tree cp_parser_lookup_name_simple (cp_parser *, tree, location_t); @@ -2420,7 +2420,7 @@ static bool cp_parser_check_declarator_template_parameters (cp_parser *, cp_declarator *, location_t); static bool cp_parser_check_template_parameters (cp_parser *, unsigned, location_t, cp_declarator *); -static tree cp_parser_simple_cast_expression +static cp_expr cp_parser_simple_cast_expression (cp_parser *); static tree cp_parser_global_scope_opt (cp_parser *, bool); @@ -3666,7 +3666,7 @@ cp_parser_pop_lexer (cp_parser *parser) /* Parse an identifier. Returns an IDENTIFIER_NODE representing the identifier. */ -static tree +static cp_expr cp_parser_identifier (cp_parser* parser) { cp_token *token; @@ -3674,7 +3674,10 @@ cp_parser_identifier (cp_parser* parser) /* Look for the identifier. */ token = cp_parser_require (parser, CPP_NAME, RT_NAME); /* Return the value. */ - return token ? token->u.value : error_mark_node; + if (token) + return cp_expr (token->u.value, token->location); + else + return error_mark_node; } /* Parse a sequence of adjacent string constants. Returns a @@ -3691,7 +3694,7 @@ cp_parser_identifier (cp_parser* parser) This code is largely lifted from lex_string() in c-lex.c. FUTURE: ObjC++ will need to handle @-strings here. */ -static tree +static cp_expr cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok, bool lookup_udlit = true) { @@ -3713,6 +3716,8 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok, return error_mark_node; } + location_t loc = tok->location; + if (cpp_userdef_string_p (tok->type)) { string_tree = USERDEF_LITERAL_VALUE (tok->u.value); @@ -3750,11 +3755,13 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok, } else { + location_t last_tok_loc; gcc_obstack_init (&str_ob); count = 0; do { + last_tok_loc = tok->location; cp_lexer_consume_token (parser->lexer); count++; str.text = (const unsigned char *)TREE_STRING_POINTER (string_tree); @@ -3809,6 +3816,13 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok, } while (cp_parser_is_string_literal (tok)); + /* A string literal built by concatenation has its caret=start at + the start of the initial string, and its finish at the finish of + the final string literal. */ + loc = make_location (loc, loc, + get_range_from_loc (line_table, + last_tok_loc).m_finish); + strs = (cpp_string *) obstack_finish (&str_ob); } @@ -3861,7 +3875,7 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok, if (count > 1) obstack_free (&str_ob, 0); - return value; + return cp_expr (value, loc); } /* Look up a literal operator with the name and the exact arguments. */ @@ -3912,7 +3926,7 @@ lookup_literal_operator (tree name, vec *args) /* Parse a user-defined char constant. Returns a call to a user-defined literal operator taking the character as an argument. */ -static tree +static cp_expr cp_parser_userdef_char_literal (cp_parser *parser) { cp_token *token = cp_lexer_consume_token (parser->lexer); @@ -4004,7 +4018,7 @@ make_string_pack (tree value) /* Parse a user-defined numeric constant. returns a call to a user-defined literal operator. */ -static tree +static cp_expr cp_parser_userdef_numeric_literal (cp_parser *parser) { cp_token *token = cp_lexer_consume_token (parser->lexer); @@ -4405,7 +4419,7 @@ cp_parser_fold_operator (cp_parser *parser) Note that the '(' and ')' are matched in primary expression. */ -static tree +static cp_expr cp_parser_fold_expression (cp_parser *parser, tree expr1) { cp_id_kind pidk; @@ -4525,7 +4539,7 @@ cp_parser_fold_expression (cp_parser *parser, tree expr1) Returns a representation of the expression. Upon return, *IDK indicates what kind of id-expression (if any) was present. */ -static tree +static cp_expr cp_parser_primary_expression (cp_parser *parser, bool address_p, bool cast_p, @@ -4610,7 +4624,7 @@ cp_parser_primary_expression (cp_parser *parser, if (!cast_p) cp_parser_non_integral_constant_expression (parser, NIC_FLOAT); } - return token->u.value; + return cp_expr (token->u.value, token->location); case CPP_CHAR_USERDEF: case CPP_CHAR16_USERDEF: @@ -4668,9 +4682,11 @@ cp_parser_primary_expression (cp_parser *parser, } /* Otherwise it's a normal parenthesized expression. */ { - tree expr; + cp_expr expr; bool saved_greater_than_is_operator_p; + location_t open_paren_loc = token->location; + /* Consume the `('. */ cp_lexer_consume_token (parser->lexer); /* Within a parenthesized expression, a `>' token is always @@ -4715,7 +4731,11 @@ cp_parser_primary_expression (cp_parser *parser, template-parameter-list now. */ parser->greater_than_is_operator_p = saved_greater_than_is_operator_p; + /* Consume the `)'. */ + token = cp_lexer_peek_token (parser->lexer); + location_t close_paren_loc = token->location; + expr.set_range (open_paren_loc, close_paren_loc); if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN) && !cp_parser_uncommitted_to_tentative_parse_p (parser)) cp_parser_skip_to_end_of_statement (parser); @@ -4735,7 +4755,7 @@ cp_parser_primary_expression (cp_parser *parser, return msg; /* ... else, fall though to see if it's a lambda. */ } - tree lam = cp_parser_lambda_expression (parser); + cp_expr lam = cp_parser_lambda_expression (parser); /* Don't warn about a failed tentative parse. */ if (cp_parser_error_occurred (parser)) return error_mark_node; @@ -4756,20 +4776,20 @@ cp_parser_primary_expression (cp_parser *parser, /* These two are the boolean literals. */ case RID_TRUE: cp_lexer_consume_token (parser->lexer); - return boolean_true_node; + return cp_expr (boolean_true_node, token->location); case RID_FALSE: cp_lexer_consume_token (parser->lexer); - return boolean_false_node; + return cp_expr (boolean_false_node, token->location); /* The `__null' literal. */ case RID_NULL: cp_lexer_consume_token (parser->lexer); - return null_node; + return cp_expr (null_node, token->location); /* The `nullptr' literal. */ case RID_NULLPTR: cp_lexer_consume_token (parser->lexer); - return nullptr_node; + return cp_expr (nullptr_node, token->location); /* Recognize the `this' keyword. */ case RID_THIS: @@ -4917,14 +4937,14 @@ cp_parser_primary_expression (cp_parser *parser, case CPP_TEMPLATE_ID: case CPP_NESTED_NAME_SPECIFIER: { - tree id_expression; - tree decl; + id_expression: + cp_expr id_expression; + cp_expr decl; const char *error_msg; bool template_p; bool done; cp_token *id_expr_token; - id_expression: /* Parse the id-expression. */ id_expression = cp_parser_id_expression (parser, @@ -4992,9 +5012,13 @@ cp_parser_primary_expression (cp_parser *parser, return objc_build_class_component_ref (id_expression, component); } + /* FIXME: disabling for now, as it strips location info, and I'd + prefer not to expose cp_expr to c-family for now. */ +#if 0 /* In Objective-C++, an instance variable (ivar) may be preferred to whatever cp_parser_lookup_name() found. */ decl = objc_lookup_ivar (decl, id_expression); +#endif /* If name lookup gives us a SCOPE_REF, then the qualifying scope was dependent. */ @@ -5035,7 +5059,7 @@ cp_parser_primary_expression (cp_parser *parser, { error_at (id_expr_token->location, "local variable %qD may not appear in this context", - decl); + decl.get_value ()); return error_mark_node; } } @@ -5063,7 +5087,7 @@ cp_parser_primary_expression (cp_parser *parser, } } -static inline tree +static inline cp_expr cp_parser_primary_expression (cp_parser *parser, bool address_p, bool cast_p, @@ -5108,7 +5132,7 @@ cp_parser_primary_expression (cp_parser *parser, If DECLARATOR_P is true, the id-expression is appearing as part of a declarator, rather than as part of an expression. */ -static tree +static cp_expr cp_parser_id_expression (cp_parser *parser, bool template_keyword_p, bool check_dependency_p, @@ -5243,7 +5267,7 @@ cp_parser_id_expression (cp_parser *parser, is true, the unqualified-id is appearing as part of a declarator, rather than as part of an expression. */ -static tree +static cp_expr cp_parser_unqualified_id (cp_parser* parser, bool template_keyword_p, bool check_dependency_p, @@ -5506,7 +5530,7 @@ cp_parser_unqualified_id (cp_parser* parser, case CPP_KEYWORD: if (token->keyword == RID_OPERATOR) { - tree id; + cp_expr id; /* This could be a template-id, so we try that first. */ cp_parser_parse_tentatively (parser); @@ -6096,7 +6120,7 @@ cp_parser_compound_literal_p (cp_parser *parser) Returns a representation of the expression. */ -static tree +static cp_expr cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, bool member_access_only_p, bool decltype_p, cp_id_kind * pidk_return) @@ -6105,13 +6129,15 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, location_t loc; enum rid keyword; cp_id_kind idk = CP_ID_KIND_NONE; - tree postfix_expression = NULL_TREE; + cp_expr postfix_expression = NULL_TREE; bool is_member_access = false; int saved_in_statement = -1; /* Peek at the next token. */ token = cp_lexer_peek_token (parser->lexer); loc = token->location; + location_t start_loc = get_range_from_loc (line_table, loc).m_start; + /* Some of the productions are determined by keywords. */ keyword = token->keyword; switch (keyword) @@ -6122,7 +6148,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, case RID_CONSTCAST: { tree type; - tree expression; + cp_expr expression; const char *saved_message; bool saved_in_type_id_in_expr_p; @@ -6155,7 +6181,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, /* And the expression which is being cast. */ cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN); expression = cp_parser_expression (parser, & idk, /*cast_p=*/true); - cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN); + location_t end_loc = cp_parser_require (parser, CPP_CLOSE_PAREN, + RT_CLOSE_PAREN)->location; parser->greater_than_is_operator_p = saved_greater_than_is_operator_p; @@ -6188,6 +6215,11 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, default: gcc_unreachable (); } + + /* Construct a location ranging from the start of the "*_cast" token + to the final closing paren, with the caret at the start. */ + location_t cp_cast_loc = make_location (start_loc, start_loc, end_loc); + postfix_expression.set_location (cp_cast_loc); } break; @@ -6467,6 +6499,9 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, postfix_expression, false, decltype_p); + postfix_expression.set_range (start_loc, + postfix_expression.get_location ()); + idk = CP_ID_KIND_NONE; is_member_access = false; break; @@ -6480,6 +6515,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, bool saved_non_integral_constant_expression_p = false; tsubst_flags_t complain = complain_flags (decltype_p); vec *args; + location_t close_paren_loc; is_member_access = false; @@ -6499,7 +6535,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, (parser, non_attr, /*cast_p=*/false, /*allow_expansion_p=*/true, /*non_constant_p=*/NULL, - /*want_literal_zero_p=*/warn_memset_transposed_args)); + /*want_literal_zero_p=*/warn_memset_transposed_args, + /*close_paren_loc=*/&close_paren_loc)); if (is_builtin_constant_p) { parser->integral_constant_expression_p @@ -6649,7 +6686,10 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, koenig_p, complain); - protected_set_expr_location (postfix_expression, token->location); + location_t combined_loc = make_location (token->location, + start_loc, + close_paren_loc); + protected_set_expr_location (postfix_expression, combined_loc); /* The POSTFIX_EXPRESSION is certainly no longer an id. */ idk = CP_ID_KIND_NONE; @@ -6710,7 +6750,9 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, if (pidk_return != NULL) * pidk_return = idk; if (member_access_only_p) - return is_member_access? postfix_expression : error_mark_node; + return is_member_access + ? postfix_expression + : cp_expr (error_mark_node); else return postfix_expression; } @@ -6910,7 +6952,7 @@ cp_parser_postfix_open_square_expression (cp_parser *parser, static tree cp_parser_postfix_dot_deref_expression (cp_parser *parser, enum cpp_ttype token_type, - tree postfix_expression, + cp_expr postfix_expression, bool for_offsetof, cp_id_kind *idk, location_t location) { @@ -6947,7 +6989,7 @@ cp_parser_postfix_dot_deref_expression (cp_parser *parser, if (scope == unknown_type_node) { error_at (location, "%qE does not have class type", - postfix_expression); + postfix_expression.get_value ()); scope = NULL_TREE; } /* Unlike the object expression in other contexts, *this is not @@ -7120,7 +7162,8 @@ cp_parser_parenthesized_expression_list (cp_parser* parser, bool cast_p, bool allow_expansion_p, bool *non_constant_p, - bool want_literal_zero_p) + bool want_literal_zero_p, + location_t *close_paren_loc) { vec *expression_list; bool fold_expr_p = is_attribute_list != non_attr; @@ -7267,6 +7310,9 @@ cp_parser_parenthesized_expression_list (cp_parser* parser, cp_lexer_consume_token (parser->lexer); } + if (close_paren_loc) + *close_paren_loc = cp_lexer_peek_token (parser->lexer)->location; + if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)) { int ending; @@ -7436,7 +7482,7 @@ cp_parser_pseudo_destructor_name (cp_parser* parser, Returns a representation of the expression. */ -static tree +static cp_expr cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk, bool address_p, bool cast_p, bool decltype_p) { @@ -7650,8 +7696,8 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk, } if (unary_operator != ERROR_MARK) { - tree cast_expression; - tree expression = error_mark_node; + cp_expr cast_expression; + cp_expr expression = error_mark_node; non_integral_constant non_constant_p = NIC_NONE; location_t loc = token->location; tsubst_flags_t complain = complain_flags (decltype_p); @@ -7665,6 +7711,11 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk, /*cast_p=*/false, /*decltype*/false, pidk); + + /* Make a location starting at the token (the caret), and + extending to the end of the cast_expression. */ + loc = make_location (loc, loc, cast_expression.get_finish ()); + /* Now, build an appropriate representation. */ switch (unary_operator) { @@ -7700,6 +7751,8 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk, gcc_unreachable (); } + protected_set_expr_location (expression, loc); + if (non_constant_p != NIC_NONE && cp_parser_non_integral_constant_expression (parser, non_constant_p)) @@ -7763,6 +7816,8 @@ cp_parser_new_expression (cp_parser* parser) tree nelts = NULL_TREE; tree ret; + location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; + /* Look for the optional `::' operator. */ global_scope_p = (cp_parser_global_scope_opt (parser, @@ -7857,6 +7912,11 @@ cp_parser_new_expression (cp_parser* parser) if (initializer != NULL) release_tree_vector (initializer); + /* FIXME: better location for result; currently just the location of + the initial token. + There doesn't seem to be an easy way to get at the last consumed + token from the lexer, which would give us the finish of the range. */ + protected_set_expr_location (ret, start_loc); return ret; } @@ -8229,7 +8289,7 @@ cp_parser_tokens_start_cast_expression (cp_parser *parser) Returns a representation of the expression. */ -static tree +static cp_expr cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p, bool decltype_p, cp_id_kind * pidk) { @@ -8237,7 +8297,7 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p, if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) { tree type = NULL_TREE; - tree expr = NULL_TREE; + cp_expr expr (NULL_TREE); int cast_expression = 0; const char *saved_message; @@ -8250,7 +8310,9 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p, parser->type_definition_forbidden_message = G_("types may not be defined in casts"); /* Consume the `('. */ - cp_lexer_consume_token (parser->lexer); + cp_token *open_paren = cp_lexer_consume_token (parser->lexer); + location_t open_paren_loc = open_paren->location; + /* A very tricky bit is that `(struct S) { 3 }' is a compound-literal (which we permit in C++ as an extension). But, that construct is not a cast-expression -- it is a @@ -8352,7 +8414,12 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p, return error_mark_node; /* Perform the cast. */ - expr = build_c_cast (input_location, type, expr); + /* Make a location starting at the open paren (the caret), and + extending to the end of "expr". */ + location_t cast_loc = make_location (open_paren_loc, + open_paren_loc, + expr.get_finish ()); + expr = build_c_cast (cast_loc, type, expr); return expr; } } @@ -8445,7 +8512,7 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p, ? PREC_NOT_OPERATOR \ : binops_by_token[token->type].prec) -static tree +static cp_expr cp_parser_binary_expression (cp_parser* parser, bool cast_p, bool no_toplevel_fold_p, bool decltype_p, @@ -8455,7 +8522,7 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p, cp_parser_expression_stack stack; cp_parser_expression_stack_entry *sp = &stack[0]; cp_parser_expression_stack_entry current; - tree rhs; + cp_expr rhs; cp_token *token; enum tree_code rhs_type; enum cp_parser_prec new_prec, lookahead_prec; @@ -8591,6 +8658,11 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p, maybe_constant_value (rhs)); overload = NULL; + + location_t combined_loc = make_location (current.loc, + current.lhs.get_start (), + rhs.get_finish ()); + /* ??? Currently we pass lhs_type == ERROR_MARK and rhs_type == ERROR_MARK for everything that is not a binary expression. This makes warn_about_parentheses miss some warnings that @@ -8601,18 +8673,18 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p, if (no_toplevel_fold_p && lookahead_prec <= current.prec && sp == stack) - current.lhs = build2 (current.tree_type, - TREE_CODE_CLASS (current.tree_type) - == tcc_comparison - ? boolean_type_node : TREE_TYPE (current.lhs), - current.lhs, rhs); + current.lhs = build2_loc (combined_loc, + current.tree_type, + TREE_CODE_CLASS (current.tree_type) + == tcc_comparison + ? boolean_type_node : TREE_TYPE (current.lhs), + current.lhs, rhs); else - current.lhs = build_x_binary_op (current.loc, current.tree_type, + current.lhs = build_x_binary_op (combined_loc, current.tree_type, current.lhs, current.lhs_type, rhs, rhs_type, &overload, complain_flags (decltype_p)); current.lhs_type = current.tree_type; - protected_set_expr_location (current.lhs, current.loc); /* If the binary operator required the use of an overloaded operator, then this expression cannot be an integral constant-expression. @@ -8629,7 +8701,7 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p, return current.lhs; } -static tree +static cp_expr cp_parser_binary_expression (cp_parser* parser, bool cast_p, bool no_toplevel_fold_p, enum cp_parser_prec prec, @@ -8653,10 +8725,10 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p, ? : assignment-expression */ static tree -cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr) +cp_parser_question_colon_clause (cp_parser* parser, cp_expr logical_or_expr) { tree expr; - tree assignment_expr; + cp_expr assignment_expr; struct cp_token *token; location_t loc = cp_lexer_peek_token (parser->lexer)->location; @@ -8692,6 +8764,12 @@ cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr) assignment_expr = cp_parser_assignment_expression (parser); c_inhibit_evaluation_warnings -= logical_or_expr == truthvalue_true_node; + /* Make a location with the caret at the "?", ranging from the start of + the logical_or_expr to the end of the assignment_expr. */ + loc = make_location (loc, + logical_or_expr.get_start (), + assignment_expr.get_finish ()); + /* Build the conditional-expression. */ return build_x_conditional_expr (loc, logical_or_expr, expr, @@ -8711,11 +8789,11 @@ cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr) Returns a representation for the expression. */ -static tree +static cp_expr cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk, bool cast_p, bool decltype_p) { - tree expr; + cp_expr expr; /* If the next token is the `throw' keyword, then we're looking at a throw-expression. */ @@ -8747,7 +8825,8 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk, location_t saved_input_location; /* Parse the right-hand side of the assignment. */ - tree rhs = cp_parser_initializer_clause (parser, &non_constant_p); + cp_expr rhs = cp_parser_initializer_clause (parser, + &non_constant_p); if (BRACE_ENCLOSED_INITIALIZER_P (rhs)) maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); @@ -8758,13 +8837,19 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk, NIC_ASSIGNMENT)) return error_mark_node; /* Build the assignment expression. Its default - location is the location of the '=' token. */ + location is the location of the '=' token as the + caret, ranging from the start of the lhs to the + end of the rhs. */ saved_input_location = input_location; + loc = make_location (loc, + expr.get_start (), + rhs.get_finish ()); input_location = loc; expr = build_x_modify_expr (loc, expr, assignment_operator, rhs, complain_flags (decltype_p)); + protected_set_expr_location (expr, loc); input_location = saved_input_location; } } @@ -8874,16 +8959,16 @@ cp_parser_assignment_operator_opt (cp_parser* parser) Returns a representation of the expression. */ -static tree +static cp_expr cp_parser_expression (cp_parser* parser, cp_id_kind * pidk, bool cast_p, bool decltype_p) { - tree expression = NULL_TREE; + cp_expr expression = NULL_TREE; location_t loc = UNKNOWN_LOCATION; while (true) { - tree assignment_expression; + cp_expr assignment_expression; /* Parse the next assignment-expression. */ assignment_expression @@ -8905,9 +8990,17 @@ cp_parser_expression (cp_parser* parser, cp_id_kind * pidk, if (!expression) expression = assignment_expression; else - expression = build_x_compound_expr (loc, expression, - assignment_expression, - complain_flags (decltype_p)); + { + /* Create a location with caret at the comma, ranging + from the start of the LHS to the end of the RHS. */ + loc = make_location (loc, + expression.get_start (), + assignment_expression.get_finish ()); + expression = build_x_compound_expr (loc, expression, + assignment_expression, + complain_flags (decltype_p)); + expression.set_location (loc); + } /* If the next token is not a comma, or we're in a fold-expression, then we are done with the expression. */ if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA) @@ -8934,7 +9027,7 @@ cp_parser_expression (cp_parser* parser, cp_id_kind * pidk, constant, *NON_CONSTANT_P is set to TRUE. If ALLOW_NON_CONSTANT_P is false, NON_CONSTANT_P should be NULL. */ -static tree +static cp_expr cp_parser_constant_expression (cp_parser* parser, bool allow_non_constant_p, bool *non_constant_p) @@ -8942,7 +9035,7 @@ cp_parser_constant_expression (cp_parser* parser, bool saved_integral_constant_expression_p; bool saved_allow_non_integral_constant_expression_p; bool saved_non_integral_constant_expression_p; - tree expression; + cp_expr expression; /* It might seem that we could simply parse the conditional-expression, and then check to see if it were @@ -9312,7 +9405,7 @@ finish_lambda_scope (void) Returns a representation of the expression. */ -static tree +static cp_expr cp_parser_lambda_expression (cp_parser* parser) { tree lambda_expr = build_lambda_expr (); @@ -13314,7 +13407,7 @@ cp_parser_mem_initializer_id (cp_parser* parser) Returns an IDENTIFIER_NODE for the operator which is a human-readable spelling of the identifier, e.g., `operator +'. */ -static tree +static cp_expr cp_parser_operator_function_id (cp_parser* parser) { /* Look for the `operator' keyword. */ @@ -13354,7 +13447,7 @@ cp_literal_operator_id (const char* name) Returns an IDENTIFIER_NODE for the operator which is a human-readable spelling of the identifier, e.g., `operator +'. */ -static tree +static cp_expr cp_parser_operator (cp_parser* parser) { tree id = NULL_TREE; @@ -13363,6 +13456,9 @@ cp_parser_operator (cp_parser* parser) /* Peek at the next token. */ token = cp_lexer_peek_token (parser->lexer); + + location_t start_loc = token->location; + /* Figure out which operator we have. */ switch (token->type) { @@ -13379,7 +13475,7 @@ cp_parser_operator (cp_parser* parser) break; /* Consume the `new' or `delete' token. */ - cp_lexer_consume_token (parser->lexer); + location_t end_loc = cp_lexer_consume_token (parser->lexer)->location; /* Peek at the next token. */ token = cp_lexer_peek_token (parser->lexer); @@ -13390,7 +13486,8 @@ cp_parser_operator (cp_parser* parser) /* Consume the `[' token. */ cp_lexer_consume_token (parser->lexer); /* Look for the `]' token. */ - cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE); + end_loc = cp_parser_require (parser, CPP_CLOSE_SQUARE, + RT_CLOSE_SQUARE)->location; id = ansi_opname (op == NEW_EXPR ? VEC_NEW_EXPR : VEC_DELETE_EXPR); } @@ -13398,7 +13495,9 @@ cp_parser_operator (cp_parser* parser) else id = ansi_opname (op); - return id; + location_t loc = make_location (start_loc, start_loc, end_loc); + + return cp_expr (id, loc); } case CPP_PLUS: @@ -13644,7 +13743,7 @@ cp_parser_operator (cp_parser* parser) id = error_mark_node; } - return id; + return cp_expr (id, start_loc); } /* Parse a template-declaration. @@ -20318,10 +20417,10 @@ cp_parser_initializer (cp_parser* parser, bool* is_direct_init, Otherwise, calls cp_parser_braced_list. */ -static tree +static cp_expr cp_parser_initializer_clause (cp_parser* parser, bool* non_constant_p) { - tree initializer; + cp_expr initializer; /* Assume the expression is constant. */ *non_constant_p = false; @@ -24088,7 +24187,7 @@ cp_parser_nested_requirement (cp_parser *parser) TREE_LIST of candidates if name-lookup results in an ambiguity, and NULL_TREE otherwise. */ -static tree +static cp_expr cp_parser_lookup_name (cp_parser *parser, tree name, enum tag_types tag_type, bool is_template, @@ -24330,7 +24429,7 @@ cp_parser_lookup_name (cp_parser *parser, tree name, maybe_record_typedef_use (decl); - return decl; + return cp_expr (decl, name_location); } /* Like cp_parser_lookup_name, but for use in the typical case where @@ -25332,7 +25431,7 @@ cp_parser_single_declaration (cp_parser* parser, /* Parse a cast-expression that is not the operand of a unary "&". */ -static tree +static cp_expr cp_parser_simple_cast_expression (cp_parser *parser) { return cp_parser_cast_expression (parser, /*address_p=*/false, diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index c1f4330..0d0b053 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -2159,8 +2159,8 @@ empty_expr_stmt_p (tree expr_stmt) the function (or functions) to call; ARGS are the arguments to the call. Returns the functions to be considered by overload resolution. */ -tree -perform_koenig_lookup (tree fn, vec *args, +cp_expr +perform_koenig_lookup (cp_expr fn, vec *args, tsubst_flags_t complain) { tree identifier = NULL_TREE; @@ -2455,10 +2455,15 @@ finish_call_expr (tree fn, vec **args, bool disallow_virtual, is indicated by CODE, which should be POSTINCREMENT_EXPR or POSTDECREMENT_EXPR.) */ -tree -finish_increment_expr (tree expr, enum tree_code code) +cp_expr +finish_increment_expr (cp_expr expr, enum tree_code code) { - return build_x_unary_op (input_location, code, expr, tf_warning_or_error); + /* input_location holds the location of the trailing operator token. */ + cp_expr result = build_x_unary_op (input_location, code, expr, + tf_warning_or_error); + result.set_range (expr.get_start (), + get_range_from_loc (line_table, input_location).m_finish); + return result; } /* Finish a use of `this'. Returns an expression for `this'. */ @@ -2552,16 +2557,18 @@ finish_pseudo_destructor_expr (tree object, tree scope, tree destructor, /* Finish an expression of the form CODE EXPR. */ -tree -finish_unary_op_expr (location_t loc, enum tree_code code, tree expr, +cp_expr +finish_unary_op_expr (location_t op_loc, enum tree_code code, cp_expr expr, tsubst_flags_t complain) { - tree result = build_x_unary_op (loc, code, expr, complain); + location_t combined_loc = make_location (op_loc, + op_loc, expr.get_finish ()); + tree result = build_x_unary_op (combined_loc, code, expr, complain); if ((complain & tf_warning) && TREE_OVERFLOW_P (result) && !TREE_OVERFLOW_P (expr)) overflow_warning (input_location, result); - return result; + return cp_expr (result, combined_loc); } /* Finish a compound-literal expression. TYPE is the type to which @@ -3302,7 +3309,7 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain) the use of "this" explicit. Upon return, *IDK will be filled in appropriately. */ -tree +cp_expr finish_id_expression (tree id_expression, tree decl, tree scope, @@ -3647,7 +3654,7 @@ finish_id_expression (tree id_expression, } } - return decl; + return cp_expr (decl, location); } /* Implement the __typeof keyword: Return the type of EXPR, suitable for diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 9e6f949..1cdbfbc 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -2269,7 +2269,7 @@ lookup_anon_field (tree t, tree type) functions indicated by MEMBER. */ tree -build_class_member_access_expr (tree object, tree member, +build_class_member_access_expr (cp_expr object, tree member, tree access_path, bool preserve_reference, tsubst_flags_t complain) { @@ -2299,10 +2299,10 @@ build_class_member_access_expr (tree object, tree member, && CLASS_TYPE_P (TREE_TYPE (object_type))) error ("request for member %qD in %qE, which is of pointer " "type %qT (maybe you meant to use %<->%> ?)", - member, object, object_type); + member, object.get_value (), object_type); else error ("request for member %qD in %qE, which is of non-class " - "type %qT", member, object, object_type); + "type %qT", member, object.get_value (), object_type); } return error_mark_node; } @@ -2443,7 +2443,12 @@ build_class_member_access_expr (tree object, tree member, member_type = cp_build_qualified_type (member_type, type_quals); } - result = build3_loc (input_location, COMPONENT_REF, member_type, + location_t combined_loc = + make_location (input_location, + object.get_start (), + get_range_from_loc (line_table, + input_location).m_finish); + result = build3_loc (combined_loc, COMPONENT_REF, member_type, object, member, NULL_TREE); result = fold_if_not_in_template (result); @@ -2633,7 +2638,7 @@ check_template_keyword (tree decl) be a template via the use of the "A::template B" syntax. */ tree -finish_class_member_access_expr (tree object, tree name, bool template_p, +finish_class_member_access_expr (cp_expr object, tree name, bool template_p, tsubst_flags_t complain) { tree expr; @@ -2667,7 +2672,7 @@ finish_class_member_access_expr (tree object, tree name, bool template_p, && TYPE_P (TREE_OPERAND (name, 0)) && dependent_type_p (TREE_OPERAND (name, 0)))) return build_min_nt_loc (UNKNOWN_LOCATION, COMPONENT_REF, - object, name, NULL_TREE); + object.get_value (), name, NULL_TREE); object = build_non_dependent_expr (object); } else if (c_dialect_objc () @@ -2690,10 +2695,10 @@ finish_class_member_access_expr (tree object, tree name, bool template_p, && CLASS_TYPE_P (TREE_TYPE (object_type))) error ("request for member %qD in %qE, which is of pointer " "type %qT (maybe you meant to use %<->%> ?)", - name, object, object_type); + name, object.get_value (), object_type); else error ("request for member %qD in %qE, which is of non-class " - "type %qT", name, object, object_type); + "type %qT", name, object.get_value (), object_type); } return error_mark_node; } @@ -5108,7 +5113,7 @@ cp_build_binary_op (location_t location, instrument_expr = ubsan_instrument_shift (location, code, op0, op1); } - result = build2 (resultcode, build_type, op0, op1); + result = build2_loc (location, resultcode, build_type, op0, op1); result = fold_if_not_in_template (result); if (final_type != 0) result = cp_convert (final_type, result, complain); @@ -5255,7 +5260,7 @@ pointer_diff (tree op0, tree op1, tree ptrtype, tsubst_flags_t complain) and XARG is the operand. */ tree -build_x_unary_op (location_t loc, enum tree_code code, tree xarg, +build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg, tsubst_flags_t complain) { tree orig_expr = xarg; @@ -5265,7 +5270,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg, if (processing_template_decl) { if (type_dependent_expression_p (xarg)) - return build_min_nt_loc (loc, code, xarg, NULL_TREE); + return build_min_nt_loc (loc, code, xarg.get_value (), NULL_TREE); xarg = build_non_dependent_expr (xarg); } @@ -5300,7 +5305,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg, error (DECL_CONSTRUCTOR_P (fn) ? G_("taking address of constructor %qE") : G_("taking address of destructor %qE"), - xarg); + xarg.get_value ()); return error_mark_node; } } @@ -5316,7 +5321,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg, if (complain & tf_error) { error ("invalid use of %qE to form a " - "pointer-to-member-function", xarg); + "pointer-to-member-function", xarg.get_value ()); if (TREE_CODE (xarg) != OFFSET_REF) inform (input_location, " a qualified-id is required"); } @@ -5327,7 +5332,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg, if (complain & tf_error) error ("parentheses around %qE cannot be used to form a" " pointer-to-member-function", - xarg); + xarg.get_value ()); else return error_mark_node; PTRMEM_OK_P (xarg) = 1; @@ -5424,7 +5429,7 @@ build_nop (tree type, tree expr) { if (type == error_mark_node || error_operand_p (expr)) return expr; - return build1 (NOP_EXPR, type, expr); + return build1_loc (EXPR_LOCATION (expr), NOP_EXPR, type, expr); } /* Take the address of ARG, whatever that means under C++ semantics. @@ -7246,9 +7251,23 @@ build_const_cast (tree type, tree expr, tsubst_flags_t complain) /* Like cp_build_c_cast, but for the c-common bits. */ tree -build_c_cast (location_t /*loc*/, tree type, tree expr) +build_c_cast (location_t loc, tree type, tree expr) { - return cp_build_c_cast (type, expr, tf_warning_or_error); + tree result = cp_build_c_cast (type, expr, tf_warning_or_error); + protected_set_expr_location (result, loc); + return result; +} + +/* Like the "build_c_cast" used for c-common, but using cp_expr to + preserve location information even for tree nodes that don't + support it. */ + +cp_expr +build_c_cast (location_t loc, tree type, cp_expr expr) +{ + tree result = cp_build_c_cast (type, expr, tf_warning_or_error); + protected_set_expr_location (result, loc); + return cp_expr (result, loc); } /* Build an expression representing an explicit C-style cast to type @@ -7756,7 +7775,7 @@ cp_build_modify_expr (tree lhs, enum tree_code modifycode, tree rhs, return result; } -tree +cp_expr build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, tree rhs, tsubst_flags_t complain) { @@ -7776,7 +7795,10 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, return rval; } } - return cp_build_modify_expr (lhs, modifycode, rhs, complain); + + tree result = cp_build_modify_expr (lhs, modifycode, rhs, complain); + protected_set_expr_location (result, loc); + return result; } /* Helper function for get_delta_difference which assumes FROM is a base diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index 2c9143e..88e482c 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -1739,7 +1739,9 @@ build_x_arrow (location_t loc, tree expr, tsubst_flags_t complain) return expr; } - return cp_build_indirect_ref (last_rval, RO_NULL, complain); + tree result = cp_build_indirect_ref (last_rval, RO_NULL, complain); + protected_set_expr_location (result, loc); + return result; } if (complain & tf_error) diff --git a/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.c b/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.c new file mode 100644 index 0000000..e34a66a --- /dev/null +++ b/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.c @@ -0,0 +1,535 @@ +/* { 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 "" } */ + + __emit_expression_range (0, *(int *)0xdeadbeef ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, *(int *)0xdeadbeef ); + ^~~~~~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ + +} + +/* Binary operators. *******************************************/ + +void test_multiplicative_operators (int lhs, int rhs) +{ + __emit_expression_range (0, lhs * rhs ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, lhs * rhs ); + ~~~~^~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, lhs / rhs ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, lhs / rhs ); + ~~~~^~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, lhs % rhs ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, lhs % rhs ); + ~~~~^~~~~ + { dg-end-multiline-output "" } */ +} + +void test_additive_operators (int lhs, int rhs) +{ + __emit_expression_range (0, lhs + rhs ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, lhs + rhs ); + ~~~~^~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, lhs - rhs ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, lhs - rhs ); + ~~~~^~~~~ + { dg-end-multiline-output "" } */ +} + +void test_shift_operators (int lhs, int rhs) +{ + __emit_expression_range (0, lhs << rhs ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, lhs << rhs ); + ~~~~^~~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, lhs >> rhs ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, lhs >> rhs ); + ~~~~^~~~~~ + { dg-end-multiline-output "" } */ +} + +void test_relational_operators (int lhs, int rhs) +{ + __emit_expression_range (0, lhs < rhs ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, lhs < rhs ); + ~~~~^~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, lhs > rhs ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, lhs > rhs ); + ~~~~^~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, lhs <= rhs ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, lhs <= rhs ); + ~~~~^~~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, lhs >= rhs ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, lhs >= rhs ); + ~~~~^~~~~~ + { dg-end-multiline-output "" } */ +} + +void test_equality_operators (int lhs, int rhs) +{ + __emit_expression_range (0, lhs == rhs ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, lhs == rhs ); + ~~~~^~~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, lhs != rhs ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, lhs != rhs ); + ~~~~^~~~~~ + { dg-end-multiline-output "" } */ +} + +void test_bitwise_binary_operators (int lhs, int rhs) +{ + __emit_expression_range (0, lhs & rhs ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, lhs & rhs ); + ~~~~^~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, lhs ^ rhs ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, lhs ^ rhs ); + ~~~~^~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, lhs | rhs ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, lhs | rhs ); + ~~~~^~~~~ + { dg-end-multiline-output "" } */ +} + +void test_logical_operators (int lhs, int rhs) +{ + __emit_expression_range (0, lhs && rhs ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, lhs && rhs ); + ~~~~^~~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, lhs || rhs ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, lhs || rhs ); + ~~~~^~~~~~ + { dg-end-multiline-output "" } */ +} + +/* Conditional operator. *******************************************/ + +void test_conditional_operators (int flag, int on_true, int on_false) +{ + __emit_expression_range (0, flag ? on_true : on_false ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, flag ? on_true : on_false ); + ~~~~~^~~~~~~~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ +} + +/* Assignment expressions. *******************************************/ + +void test_assignment_expressions (int dest, int other) +{ + __emit_expression_range (0, dest = other ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, dest = other ); + ~~~~~^~~~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, dest *= other ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, dest *= other ); + ~~~~~^~~~~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, dest /= other ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, dest /= other ); + ~~~~~^~~~~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, dest %= other ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, dest %= other ); + ~~~~~^~~~~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, dest += other ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, dest += other ); + ~~~~~^~~~~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, dest -= other ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, dest -= other ); + ~~~~~^~~~~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, dest <<= other ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, dest <<= other ); + ~~~~~^~~~~~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, dest >>= other ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, dest >>= other ); + ~~~~~^~~~~~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, dest &= other ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, dest &= other ); + ~~~~~^~~~~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, dest ^= other ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, dest ^= other ); + ~~~~~^~~~~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, dest |= other ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, dest |= other ); + ~~~~~^~~~~~~~ + { dg-end-multiline-output "" } */ +} + +/* Comma operator. *******************************************/ + +void test_comma_operator (int a, int b) +{ + __emit_expression_range (0, (a++, a + b) ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, (a++, a + b) ); + ~~~~^~~~~~~~ + { dg-end-multiline-output "" } */ +} + +/* Literals. **************************************************/ + +/* We can't test the ranges of literals directly, since the underlying + tree nodes don't retain a location. However, we can test that they + have ranges during parsing by building compound expressions using + them, and verifying the ranges of the compound expressions. */ + +void test_string_literals (int i) +{ + __emit_expression_range (0, "foo"[i] ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, "foo"[i] ); + ~~~~~~~^ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, &"foo" "bar" ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, &"foo" "bar" ); + ^~~~~~~~~~~~ + { dg-end-multiline-output "" } */ +} + +/* Examples of non-trivial expressions. ****************************/ + +extern double sqrt (double x); + +void test_quadratic (double a, double b, double c) +{ + __emit_expression_range (0, b * b - 4 * a * c ); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, b * b - 4 * a * c ); + ~~~~~~^~~~~~~~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, + (-b + sqrt (b * b - 4 * a * c)) + / (2 * a)); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + (-b + sqrt (b * b - 4 * a * c)) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + / (2 * a)); + ^~~~~~~~~ + { dg-end-multiline-output "" } */ + +} + +/* C++-specific expresssions. ****************************************/ + +void test_cp_literal_keywords (int a, int b) +{ + this; /* { dg-error "invalid use of 'this' in non-member function" } */ +/* { dg-begin-multiline-output "" } + this; + ^~~~ + { dg-end-multiline-output "" } */ + +} + +class base { + public: + base (); + base (int i); + virtual ~base (); +}; +class derived : public base { ~derived (); }; + +void test_cp_casts (base *ptr) +{ + __emit_expression_range (0, dynamic_cast (ptr)); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, dynamic_cast (ptr)); + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, static_cast (ptr)); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, static_cast (ptr)); + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, reinterpret_cast (ptr)); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, reinterpret_cast (ptr)); + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, const_cast (ptr)); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, const_cast (ptr)); + ^~~~~~~~~~~~~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ +} + +void test_new (void) +{ + /* TODO: currently these only cover the first token; see the FIXME in + cp_parser_new_expression. */ + + __emit_expression_range (0, ::new base); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, ::new base); + ^~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, new base); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, new base); + ^~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, new (base)); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, new (base)); + ^~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, new base (42)); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, new base (42)); + ^~~ + { dg-end-multiline-output "" } */ + + __emit_expression_range (0, new (base) (42)); /* { dg-warning "range" } */ +/* { dg-begin-multiline-output "" } + __emit_expression_range (0, new (base) (42)); + ^~~ + { dg-end-multiline-output "" } */ + + /* TODO: placement new. */ +} diff --git a/gcc/testsuite/g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c b/gcc/testsuite/g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c new file mode 100644 index 0000000..89cc95a --- /dev/null +++ b/gcc/testsuite/g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c @@ -0,0 +1,98 @@ +/* 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 "print-tree.h" + +int plugin_is_GPL_compatible; + +static void +emit_warning (location_t loc) +{ + source_range src_range = get_range_from_loc (line_table, loc); + warning_at (loc, 0, + "tree range %i:%i-%i:%i", + LOCATION_LINE (src_range.m_start), + LOCATION_COLUMN (src_range.m_start), + LOCATION_LINE (src_range.m_finish), + LOCATION_COLUMN (src_range.m_finish)); +} + +tree +cb_walk_tree_fn (tree * tp, int * walk_subtrees, + void * data ATTRIBUTE_UNUSED) +{ + if (TREE_CODE (*tp) != CALL_EXPR) + return NULL_TREE; + + tree call_expr = *tp; + tree fn = CALL_EXPR_FN (call_expr); + if (TREE_CODE (fn) != ADDR_EXPR) + return NULL_TREE; + fn = TREE_OPERAND (fn, 0); + if (TREE_CODE (fn) != FUNCTION_DECL) + return NULL_TREE; + if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fn)), "__emit_expression_range")) + return NULL_TREE; + + /* Get arg 1; print it! */ + tree arg = CALL_EXPR_ARG (call_expr, 1); + + emit_warning (EXPR_LOCATION (arg)); + + return NULL_TREE; +} + +static void +callback (void *gcc_data, void *user_data) +{ + tree fndecl = (tree)gcc_data; + walk_tree (&DECL_SAVED_TREE (fndecl), cb_walk_tree_fn, NULL, NULL); +} + +int +plugin_init (struct plugin_name_args *plugin_info, + struct plugin_gcc_version *version) +{ + struct register_pass_info pass_info; + const char *plugin_name = plugin_info->base_name; + int argc = plugin_info->argc; + struct plugin_argument *argv = plugin_info->argv; + + if (!plugin_default_version_check (version, &gcc_version)) + return 1; + + register_callback (plugin_name, + PLUGIN_PRE_GENERICIZE, + callback, + NULL); + + return 0; +} diff --git a/gcc/testsuite/g++.dg/plugin/plugin.exp b/gcc/testsuite/g++.dg/plugin/plugin.exp index 3ed1397..2266380 100644 --- a/gcc/testsuite/g++.dg/plugin/plugin.exp +++ b/gcc/testsuite/g++.dg/plugin/plugin.exp @@ -62,7 +62,10 @@ set plugin_test_list [list \ { dumb_plugin.c dumb-plugin-test-1.C } \ { header_plugin.c header-plugin-test.C } \ { decl_plugin.c decl-plugin-test.C } \ - { def_plugin.c def-plugin-test.C } ] + { def_plugin.c def-plugin-test.C } \ + { diagnostic_plugin_test_tree_expression_range.c \ + diagnostic-test-expressions-1.c } \ +] foreach plugin_test $plugin_test_list { # Replace each source file with its full-path name diff --git a/gcc/tree.c b/gcc/tree.c index 4ec4a38..a919cc7 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -13653,7 +13653,7 @@ nonnull_arg_p (const_tree arg) return false; } -static location_t +location_t get_pure_location (location_t loc) { if (IS_ADHOC_LOC (loc)) @@ -13680,20 +13680,20 @@ set_block (location_t loc, tree block) return COMBINE_LOCATION_DATA (line_table, pure_loc, src_range, block); } -void +location_t set_source_range (tree expr, location_t start, location_t finish) { source_range src_range; src_range.m_start = start; src_range.m_finish = finish; - set_source_range (expr, src_range); + return set_source_range (expr, src_range); } -void +location_t set_source_range (tree expr, source_range src_range) { if (!EXPR_P (expr)) - return; + return UNKNOWN_LOCATION; location_t pure_loc = get_pure_location (EXPR_LOCATION (expr)); location_t adhoc = COMBINE_LOCATION_DATA (line_table, @@ -13701,6 +13701,21 @@ set_source_range (tree expr, source_range src_range) src_range, NULL); SET_EXPR_LOCATION (expr, adhoc); + return adhoc; +} + +location_t +make_location (location_t caret, location_t start, location_t finish) +{ + location_t pure_loc = get_pure_location (caret); + source_range src_range; + src_range.m_start = start; + src_range.m_finish = finish; + location_t combined_loc = COMBINE_LOCATION_DATA (line_table, + pure_loc, + src_range, + NULL); + return combined_loc; } #include "gt-tree.h" diff --git a/gcc/tree.h b/gcc/tree.h index 7d20f74..1f32511 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -5158,6 +5158,9 @@ type_with_alias_set_p (const_tree t) } extern location_t +get_pure_location (location_t loc); + +extern location_t set_block (location_t loc, tree block); extern void gt_ggc_mx (tree &); @@ -5166,10 +5169,10 @@ extern void gt_pch_nx (tree &, gt_pointer_operator, void *); extern bool nonnull_arg_p (const_tree); -extern void +extern location_t set_source_range (tree expr, location_t start, location_t finish); -extern void +extern location_t set_source_range (tree expr, source_range src_range); static inline source_range @@ -5179,4 +5182,7 @@ get_decl_source_range (tree decl) return get_range_from_loc (line_table, loc); } +extern location_t +make_location (location_t caret, location_t start, location_t finish); + #endif /* GCC_TREE_H */