From patchwork Fri Aug 12 01:38:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 56689 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id DEA8F385AC29 for ; Fri, 12 Aug 2022 01:38:53 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org DEA8F385AC29 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1660268333; bh=i8ZM25o7YNLz6A61pcagvef9zup6jbO4hst92cOYCz0=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=kGdyMLFM6vQemgQPRm0pntffZNJFV8jUUgBQ5cB5onvD6zq92l2ndoX9PiisHOM8E FKmsTzyldbSViF9732GEKNo9ZRteIiGoVjTAOxTYI3wP2YZIKG0ph2rzb/PE0vz3uN 0R3vZ2k8mqi8lJgSeaFg+sVLii9/ZeFM9rqg5S4I= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id C9B9F3858D28 for ; Fri, 12 Aug 2022 01:38:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org C9B9F3858D28 Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-636-HNIoZhO3MrK2DQS8alawxA-1; Thu, 11 Aug 2022 21:38:15 -0400 X-MC-Unique: HNIoZhO3MrK2DQS8alawxA-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id D56051C0756F for ; Fri, 12 Aug 2022 01:38:14 +0000 (UTC) Received: from t14s.localdomain.com (unknown [10.2.17.170]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9A566112131B; Fri, 12 Aug 2022 01:38:14 +0000 (UTC) To: gcc-patches@gcc.gnu.org Subject: [PATCH] c/c++: new warning: -Wxor-used-as-pow [PR90885] Date: Thu, 11 Aug 2022 21:38:12 -0400 Message-Id: <20220812013812.3183820-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-25.5 required=5.0 tests=BAYES_00, DKIM_INVALID, DKIM_SIGNED, GIT_PATCH_0, KAM_DMARC_NONE, KAM_DMARC_STATUS, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: David Malcolm via Gcc-patches From: David Malcolm Reply-To: David Malcolm Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" PR c/90885 notes various places in real-world code where people have written C/C++ code that uses ^ (exclusive or) where presumbably they meant exponentiation. For example https://codesearch.isocpp.org/cgi-bin/cgi_ppsearch?q=2%5E32&search=Search currently finds 11 places using "2^32", and all of them appear to be places where the user means 2 to the power of 32, rather than 2 exclusive-orred with 32 (which is 34). This patch adds a new -Wxor-used-as-pow warning to the C and C++ frontends to complain about ^ when the left-hand side is the decimal constant 2 or the decimal constant 10. This is the same name as the corresponding clang warning: https://clang.llvm.org/docs/DiagnosticsReference.html#wxor-used-as-pow As per the clang warning, the warning suggests converting the left-hand side to a hexadecimal constant if you really mean xor, which suppresses the warning (and this patch implements a fix-it hint for that, whereas the clang implementation only has a fix-it hint for the initial suggestion of exponentiation). I initially tried implementing this without checking for decimals, but this version had lots of false positives. Checking for decimals requires extending the lexer to capture whether or not a CPP_NUMBER token was decimal. I added a new DECIMAL_INT flag to cpplib.h for this. Unfortunately, c_token and cp_tokens both have only an unsigned char for their flags (as captured by c_lex_with_flags), whereas this would add the 12th flag to cpp_tokens. Of the first 8 flags, all but BOL are used in the C or C++ frontends, but BOL is not, so I moved that to a higher position, using its old value for the new DECIMAL_INT flag, so that it is representable within an unsigned char. Example output: demo.c:5:13: warning: result of '2^8' is 10; did you mean '1 << 8' (256)? [-Wxor-used-as-pow] 5 | int t2_8 = 2^8; | ^ | -- | 1<< demo.c:5:12: note: you can silence this warning by using a hexadecimal constant (0x2 rather than 2) 5 | int t2_8 = 2^8; | ^ | 0x2 demo.c:21:15: warning: result of '10^6' is 12; did you mean '1e6'? [-Wxor-used-as-pow] 21 | int t10_6 = 10^6; | ^ | --- | 1e demo.c:21:13: note: you can silence this warning by using a hexadecimal constant (0xa rather than 10) 21 | int t10_6 = 10^6; | ^~ | 0xa Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu. OK for trunk? Thanks Dave gcc/c-family/ChangeLog: PR c/90885 * c-common.h (check_for_xor_used_as_pow): New decl. * c-lex.cc (c_lex_with_flags): Add DECIMAL_INT to flags as appropriate. * c-warn.cc (check_for_xor_used_as_pow): New. * c.opt (Wxor-used-as-pow): New. gcc/c/ChangeLog: PR c/90885 * c-parser.cc (c_parser_string_literal): Clear ret.m_decimal. (c_parser_expr_no_commas): Likewise. (c_parser_conditional_expression): Likewise. (c_parser_binary_expression): Clear m_decimal when popping the stack. (c_parser_unary_expression): Clear ret.m_decimal. (c_parser_has_attribute_expression): Likewise for result. (c_parser_predefined_identifier): Likewise for expr. (c_parser_postfix_expression): Likewise for expr. Set expr.m_decimal when handling a CPP_NUMBER that was a decimal token. * c-tree.h (c_expr::m_decimal): New bitfield. * c-typeck.cc (parser_build_binary_op): Clear result.m_decimal. (parser_build_binary_op): Call check_for_xor_used_as_pow. gcc/cp/ChangeLog: PR c/90885 * cp-tree.h (class cp_expr): Add bitfield m_decimal. Clear it in existing ctors. Add ctor that allows specifying its value. (cp_expr::decimal_p): New accessor. * parser.cc (cp_parser_expression_stack_entry::flags): New field. (cp_parser_primary_expression): Set m_decimal of cp_expr when handling numbers. (cp_parser_binary_expression): Extract flags from token when populating stack. Call check_for_xor_used_as_pow. gcc/ChangeLog: PR c/90885 * doc/invoke.texi (Warning Options): Add -Wxor-used-as-pow. gcc/testsuite/ChangeLog: PR c/90885 * c-c++-common/Wxor-used-as-pow-1.c: New test. * c-c++-common/Wxor-used-as-pow-fixits.c: New test. * g++.dg/parse/expr3.C: Convert 2 to 0x2 to suppress -Wxor-used-as-pow. * g++.dg/warn/Wparentheses-10.C: Likewise. * g++.dg/warn/Wparentheses-18.C: Likewise. * g++.dg/warn/Wparentheses-19.C: Likewise. * g++.dg/warn/Wparentheses-9.C: Likewise. * g++.dg/warn/Wxor-used-as-pow-named-op.C: New test. * gcc.dg/Wparentheses-6.c: Convert 2 to 0x2 to suppress -Wxor-used-as-pow. * gcc.dg/Wparentheses-7.c: Likewise. * gcc.dg/precedence-1.c: Likewise. libcpp/ChangeLog: PR c/90885 * include/cpplib.h (BOL): Move macro to 1 << 12 since it is not used by C/C++'s unsigned char token flags. (DECIMAL_INT): New, using 1 << 6, so that it is visible as part of C/C++'s 8 bits of token flags. Signed-off-by: David Malcolm --- gcc/c-family/c-common.h | 4 + gcc/c-family/c-lex.cc | 6 +- gcc/c-family/c-warn.cc | 94 +++++++++++++++++++ gcc/c-family/c.opt | 4 + gcc/c/c-parser.cc | 9 ++ gcc/c/c-tree.h | 3 + gcc/c/c-typeck.cc | 9 ++ gcc/cp/cp-tree.h | 19 +++- gcc/cp/parser.cc | 17 +++- gcc/doc/invoke.texi | 15 +++ .../c-c++-common/Wxor-used-as-pow-1.c | 57 +++++++++++ .../c-c++-common/Wxor-used-as-pow-fixits.c | 34 +++++++ gcc/testsuite/g++.dg/parse/expr3.C | 2 +- gcc/testsuite/g++.dg/warn/Wparentheses-10.C | 14 +-- gcc/testsuite/g++.dg/warn/Wparentheses-18.C | 4 +- gcc/testsuite/g++.dg/warn/Wparentheses-19.C | 12 +-- gcc/testsuite/g++.dg/warn/Wparentheses-9.C | 4 +- .../g++.dg/warn/Wxor-used-as-pow-named-op.C | 8 ++ gcc/testsuite/gcc.dg/Wparentheses-6.c | 4 +- gcc/testsuite/gcc.dg/Wparentheses-7.c | 12 +-- gcc/testsuite/gcc.dg/precedence-1.c | 4 +- libcpp/include/cpplib.h | 3 +- 22 files changed, 304 insertions(+), 34 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/Wxor-used-as-pow-1.c create mode 100644 gcc/testsuite/c-c++-common/Wxor-used-as-pow-fixits.c create mode 100644 gcc/testsuite/g++.dg/warn/Wxor-used-as-pow-named-op.C diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index c06769b6f0b..78ce94c6591 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -1475,6 +1475,10 @@ extern tree do_warn_duplicated_branches_r (tree *, int *, void *); extern void warn_for_multistatement_macros (location_t, location_t, location_t, enum rid); +extern void check_for_xor_used_as_pow (location_t lhs_loc, tree lhs_val, + location_t operator_loc, + tree rhs_val); + /* In c-attribs.cc. */ extern bool attribute_takes_identifier_p (const_tree); extern tree handle_deprecated_attribute (tree *, tree, tree, int, bool *); diff --git a/gcc/c-family/c-lex.cc b/gcc/c-family/c-lex.cc index 8bfa4f4024f..1eda6f1fccc 100644 --- a/gcc/c-family/c-lex.cc +++ b/gcc/c-family/c-lex.cc @@ -509,7 +509,11 @@ c_lex_with_flags (tree *value, location_t *loc, unsigned char *cpp_flags, /* C++ uses '0' to mark virtual functions as pure. Set PURE_ZERO to pass this information to the C++ parser. */ if (tok->val.str.len == 1 && *tok->val.str.text == '0') - add_flags = PURE_ZERO; + add_flags = PURE_ZERO | DECIMAL_INT; + else if ((flags & CPP_N_INTEGER) && (flags & CPP_N_DECIMAL)) + /* -Wxor-used-as-pow is only active for LHS of ^ expressed + as a decimal integer. */ + add_flags = DECIMAL_INT; *value = interpret_integer (tok, flags, &overflow); break; diff --git a/gcc/c-family/c-warn.cc b/gcc/c-family/c-warn.cc index ea7335f3edf..ed79cc3ca40 100644 --- a/gcc/c-family/c-warn.cc +++ b/gcc/c-family/c-warn.cc @@ -3799,3 +3799,97 @@ do_warn_array_compare (location_t location, tree_code code, tree op0, tree op1) op0, op_symbol_code (code), op1); } } + +/* Given LHS_VAL ^ RHS_VAL, where LHS_LOC is the location of the LHS and + OPERATOR_LOC is the location of the ^, complain with -Wxor-used-as-pow + if it looks like the user meant exponentiation rather than xor. */ + +void +check_for_xor_used_as_pow (location_t lhs_loc, tree lhs_val, + location_t operator_loc, + tree rhs_val) +{ + /* Only complain if both args are non-negative integer constants. */ + if (!(TREE_CODE (lhs_val) == INTEGER_CST + && tree_int_cst_sgn (lhs_val) >= 0)) + return; + if (!(TREE_CODE (rhs_val) == INTEGER_CST + && tree_int_cst_sgn (rhs_val) >= 0)) + return; + + /* Only complain if the LHS is 2 or 10. */ + unsigned HOST_WIDE_INT lhs_uhwi = tree_to_uhwi (lhs_val); + if (lhs_uhwi != 2 && lhs_uhwi != 10) + return; + + unsigned HOST_WIDE_INT rhs_uhwi = tree_to_uhwi (rhs_val); + unsigned HOST_WIDE_INT xor_result = lhs_uhwi ^ rhs_uhwi; + binary_op_rich_location loc (operator_loc, + lhs_val, rhs_val, false); + + /* If we issue fix-it hints with the warning then we will also issue a + note suggesting how to suppress the warning with a different change. + These proposed changes are incompatible. */ + loc.fixits_cannot_be_auto_applied (); + + auto_diagnostic_group d; + bool warned = false; + if (lhs_uhwi == 2) + { + /* Would exponentiation fit in int, in long long, or not at all? */ + if (rhs_uhwi < (INT_TYPE_SIZE - 1)) + { + unsigned HOST_WIDE_INT suggested_result = 1 << rhs_uhwi; + loc.add_fixit_replace (lhs_loc, "1"); + loc.add_fixit_replace (operator_loc, "<<"); + warned = warning_at (&loc, OPT_Wxor_used_as_pow, + "result of %<%wu^%wu%> is %wu;" + " did you mean %<1 << %wu%> (%wu)?", + lhs_uhwi, rhs_uhwi, xor_result, + rhs_uhwi, suggested_result); + } + else if (rhs_uhwi < (LONG_LONG_TYPE_SIZE - 1)) + { + loc.add_fixit_replace (lhs_loc, "1LL"); + loc.add_fixit_replace (operator_loc, "<<"); + warned = warning_at (&loc, OPT_Wxor_used_as_pow, + "result of %<%wu^%wu%> is %wu;" + " did you mean %<1LL << %wu%>?", + lhs_uhwi, rhs_uhwi, xor_result, + rhs_uhwi); + } + else if (rhs_uhwi <= LONG_LONG_TYPE_SIZE) + warned = warning_at (&loc, OPT_Wxor_used_as_pow, + "result of %<%wu^%wu%> is %wu;" + " did you mean exponentiation?", + lhs_uhwi, rhs_uhwi, xor_result); + /* Otherwise assume it's an xor. */ + } + else + { + gcc_assert (lhs_uhwi == 10); + loc.add_fixit_replace (lhs_loc, "1"); + loc.add_fixit_replace (operator_loc, "e"); + warned = warning_at (&loc, OPT_Wxor_used_as_pow, + "result of %<%wu^%wu%> is %wu;" + " did you mean %<1e%wu%>?", + lhs_uhwi, rhs_uhwi, xor_result, + rhs_uhwi); + } + if (warned) + { + gcc_rich_location note_loc (lhs_loc); + if (lhs_uhwi == 2) + note_loc.add_fixit_replace (lhs_loc, "0x2"); + else + { + gcc_assert (lhs_uhwi == 10); + note_loc.add_fixit_replace (lhs_loc, "0xa"); + } + note_loc.fixits_cannot_be_auto_applied (); + inform (¬e_loc, + "you can silence this warning by using a hexadecimal constant" + " (%wx rather than %wd)", + lhs_uhwi, lhs_uhwi); + } +} diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 44e1a60ce24..c11ea50ac42 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -1439,6 +1439,10 @@ Wwrite-strings C ObjC C++ ObjC++ Var(warn_write_strings) Warning In C++, nonzero means warn about deprecated conversion from string literals to 'char *'. In C, similar warning, except that the conversion is of course not deprecated by the ISO C standard. +Wxor-used-as-pow +C C++ Common Var(warn_xor_used_as_pow) Warning Init(1) +Warn about xor operators where it appears the user meant exponentiation. + Wzero-as-null-pointer-constant C++ ObjC++ Var(warn_zero_as_null_pointer_constant) Warning Warn when a literal '0' is used as null pointer. diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 92049d1a101..25064576829 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -7496,6 +7496,7 @@ c_parser_string_literal (c_parser *parser, bool translate, bool wide_ok) ret.original_code = STRING_CST; ret.original_type = NULL_TREE; set_c_expr_source_range (&ret, get_range_from_loc (line_table, loc)); + ret.m_decimal = 0; parser->seen_string_literal = true; return ret; } @@ -7575,6 +7576,7 @@ c_parser_expr_no_commas (c_parser *parser, struct c_expr *after, ret.value = build_modify_expr (op_location, lhs.value, lhs.original_type, code, exp_location, rhs.value, rhs.original_type); + ret.m_decimal = 0; set_c_expr_source_range (&ret, lhs.get_start (), rhs.get_finish ()); if (code == NOP_EXPR) ret.original_code = MODIFY_EXPR; @@ -7712,6 +7714,7 @@ c_parser_conditional_expression (c_parser *parser, struct c_expr *after, : NULL); } set_c_expr_source_range (&ret, start, exp2.get_finish ()); + ret.m_decimal = 0; return ret; } @@ -7901,6 +7904,7 @@ c_parser_binary_expression (c_parser *parser, struct c_expr *after, TREE_OPERAND (t, 0) = stack[0].expr.value; \ TREE_OPERAND (t, 1) = stack[1].expr.value; \ stack[0].expr.value = t; \ + stack[0].expr.m_decimal = 0; \ } \ else \ stack[sp - 1].expr = parser_build_binary_op (stack[sp].loc, \ @@ -8187,6 +8191,7 @@ c_parser_unary_expression (c_parser *parser) ret.value = build_indirect_ref (combined_loc, op.value, RO_UNARY_STAR); ret.src_range.m_start = op_loc; ret.src_range.m_finish = finish; + ret.m_decimal = 0; return ret; } case CPP_PLUS: @@ -8560,6 +8565,7 @@ c_parser_has_attribute_expression (c_parser *parser) result.value = boolean_false_node; set_c_expr_source_range (&result, start, finish); + result.m_decimal = 0; return result; } @@ -8925,6 +8931,7 @@ c_parser_predefined_identifier (c_parser *parser) expr.value = fname_decl (loc, c_parser_peek_token (parser)->keyword, c_parser_peek_token (parser)->value); set_c_expr_source_range (&expr, loc, loc); + expr.m_decimal = 0; c_parser_consume_token (parser); return expr; } @@ -9003,12 +9010,14 @@ c_parser_postfix_expression (c_parser *parser) source_range tok_range = c_parser_peek_token (parser)->get_range (); expr.original_code = ERROR_MARK; expr.original_type = NULL; + expr.m_decimal = 0; switch (c_parser_peek_token (parser)->type) { case CPP_NUMBER: expr.value = c_parser_peek_token (parser)->value; set_c_expr_source_range (&expr, tok_range); loc = c_parser_peek_token (parser)->location; + expr.m_decimal = c_parser_peek_token (parser)->flags & DECIMAL_INT; c_parser_consume_token (parser); if (TREE_CODE (expr.value) == FIXED_CST && !targetm.fixed_point_supported_p ()) diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index e655afd1cd4..b4231a17859 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -147,6 +147,9 @@ struct c_expr etc), so we stash a copy here. */ source_range src_range; + /* True if this was directly from a decimal constant token. */ + bool m_decimal : 1; + /* Access to the first and last locations within the source spelling of this expression. */ location_t get_start () const { return src_range.m_start; } diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index 8514488b7a5..1f5fa07c925 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -3821,6 +3821,7 @@ parser_build_binary_op (location_t location, enum tree_code code, struct c_expr arg1, struct c_expr arg2) { struct c_expr result; + result.m_decimal = 0; enum tree_code code1 = arg1.original_code; enum tree_code code2 = arg2.original_code; @@ -3978,6 +3979,14 @@ parser_build_binary_op (location_t location, enum tree_code code, "comparison between %qT and %qT", type1, type2); + if (warn_xor_used_as_pow + && code == BIT_XOR_EXPR + && arg1.m_decimal + && arg2.m_decimal) + check_for_xor_used_as_pow (arg1.get_location (), arg1.value, + location, + arg2.value); + return result; } diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 3278b4114bd..cdff8282e64 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -54,13 +54,23 @@ class cp_expr { public: cp_expr () : - m_value (NULL), m_loc (UNKNOWN_LOCATION) {} + m_value (NULL), m_loc (UNKNOWN_LOCATION), + m_decimal (false) + {} cp_expr (tree value) : - m_value (value), m_loc (cp_expr_location (m_value)) {} + m_value (value), m_loc (cp_expr_location (m_value)), + m_decimal (false) + {} cp_expr (tree value, location_t loc): - m_value (value), m_loc (loc) + m_value (value), m_loc (loc), m_decimal (false) + { + protected_set_expr_location (value, loc); + } + + cp_expr (tree value, location_t loc, bool decimal): + m_value (value), m_loc (loc), m_decimal (decimal) { protected_set_expr_location (value, loc); } @@ -102,9 +112,12 @@ public: return *this; } + bool decimal_p () const { return m_decimal; } + private: tree m_value; location_t m_loc; + bool m_decimal : 1; }; inline bool diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 33926d23179..73c46e79979 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -2066,6 +2066,8 @@ struct cp_parser_expression_stack_entry enum cp_parser_prec prec; /* Location of the binary operation we are parsing. */ location_t loc; + /* Flags from the operator token. */ + unsigned char flags; }; /* The stack for storing partial expressions. We only need NUM_PREC_VALUES @@ -5611,7 +5613,7 @@ cp_parser_primary_expression (cp_parser *parser, if (!cast_p) cp_parser_non_integral_constant_expression (parser, NIC_FLOAT); } - return (cp_expr (token->u.value, token->location) + return (cp_expr (token->u.value, token->location, token->flags & DECIMAL_INT) .maybe_add_location_wrapper ()); case CPP_CHAR_USERDEF: @@ -10157,6 +10159,7 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p, get_rhs: current.tree_type = binops_by_token[token->type].tree_type; current.loc = token->location; + current.flags = token->flags; /* We used the operator token. */ cp_lexer_consume_token (parser->lexer); @@ -10241,6 +10244,18 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p, warn_logical_not_parentheses (current.loc, current.tree_type, current.lhs, maybe_constant_value (rhs)); + if (warn_xor_used_as_pow + && current.tree_type == BIT_XOR_EXPR + /* Don't warn for named "xor" (as opposed to '^'). */ + && !(current.flags & NAMED_OP) + && current.lhs.decimal_p () + && rhs.decimal_p ()) + check_for_xor_used_as_pow + (current.lhs.get_location (), + tree_strip_any_location_wrapper (current.lhs), + current.loc, + tree_strip_any_location_wrapper (rhs)); + overload = NULL; location_t combined_loc = make_location (current.loc, diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index a17c059d515..31516ded123 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -414,6 +414,7 @@ Objective-C and Objective-C++ Dialects}. -Wvector-operation-performance @gol -Wvla -Wvla-larger-than=@var{byte-size} -Wno-vla-larger-than @gol -Wvolatile-register-var -Wwrite-strings @gol +-Wxor-used-as-pow @gol -Wzero-length-bounds} @item Static Analyzer Options @@ -9661,6 +9662,20 @@ modifier does not inhibit all optimizations that may eliminate reads and/or writes to register variables. This warning is enabled by @option{-Wall}. +@item -Wxor-used-as-pow @r{(C, C++, Objective-C and Objective-C++ only)} +@opindex Wxor-used-as-pow +@opindex Wno-xor-used-as-pow +Warn about uses of @code{^}, the exclusive or operator, where it appears +the user meant exponentiation. Specifically, the warning occurs when the +left-hand side is the decimal constant 2 or 10 and the right-hand side +is also a decimal constant. + +In C and C++, @code{^} means exclusive or, whereas in some other languages +(e.g. TeX and some versions of BASIC) it means exponentiation. + +This warning is enabled by default. It can be silenced by converting one +of the operands to hexadecimal. + @item -Wdisabled-optimization @opindex Wdisabled-optimization @opindex Wno-disabled-optimization diff --git a/gcc/testsuite/c-c++-common/Wxor-used-as-pow-1.c b/gcc/testsuite/c-c++-common/Wxor-used-as-pow-1.c new file mode 100644 index 00000000000..962902c3a05 --- /dev/null +++ b/gcc/testsuite/c-c++-common/Wxor-used-as-pow-1.c @@ -0,0 +1,57 @@ +/* The precise output depends of sizeof(int) and sizeof(long long), so + filter by target. */ +/* { dg-do compile { target i?86-*-* x86_64-*-* } } */ + +/* Apparent uses of ^ for powers of 2. */ + +short t2_16 = 2^16; /* { dg-warning "result of '2\\^16' is 18; did you mean '1 << 16' \\(65536\\)\\? \\\[-Wxor-used-as-pow\\\]" } */ +int t2_17 = 2^17; /* { dg-warning "result of '2\\^17' is 19; did you mean '1 << 17' \\(131072\\)\\?" } */ +int t2_30 = 2^30; /* { dg-warning "result of '2\\^30' is 28; did you mean '1 << 30' \\(1073741824\\)\\?" } */ + +/* Should be 1LL at 31 and above, due to overflow. */ +int t2_31 = 2^31; /* { dg-warning "result of '2\\^31' is 29; did you mean '1LL << 31'\\?" } */ +int t2_32 = 2^32; /* { dg-warning "result of '2\\^32' is 34; did you mean '1LL << 32'\\?" } */ + +long t2_63 = 2^63; /* { dg-warning "result of '2\\^63' is 61; did you mean exponentiation\\?" } */ +long t2_64 = 2^64; /* { dg-warning "result of '2\\^64' is 66; did you mean exponentiation\\?" } */ + +/* ...but don't warn when RHS is large enough. */ +long t2_65 = 2^65; +long t2_127 = 2^127; +long t2_128 = 2^128; +long t2_129 = 2^129; + +/* Verify that -Wxor-used-as-pow happens before folding. */ +long t2_32_m1 = ((2^32)-1); /* { dg-warning "result of '2\\^32' is 34; did you mean '1LL << 32'\\?" } */ + + +/* Apparent uses of ^ for powers of 10. */ + +long t10_2 = 10^2; /* { dg-warning "result of '10\\^2' is 8; did you mean '1e2'\\?" } */ +long t10_9 = 10^9; /* { dg-warning "result of '10\\^9' is 3; did you mean '1e9'\\?" } */ +long t10_10 = 10^10; /* { dg-warning "result of '10\\^10' is 0; did you mean '1e10'\\?" } */ +long t10_100 = 10^100; /* { dg-warning "result of '10\\^100' is 110; did you mean '1e100'\\?" } */ + +/* Don't warn on negatives. */ +long tm2_2 = -2^2; +long t2_m2 = 2^-2; +long tm10_10 = -10^10; +long t10_m10 = 10^-10; + +/* If LHS is not 2 or 10, we shouldn't complain. */ +int t0_0 = 0 ^ 0; +int t6_7 = 6 ^ 7; + +/* Floating point is already covered by type-checking. */ +float f10_10 = 10.f^10; /* { dg-error "invalid operands to binary \\^ \\(have 'float' and 'int'\\)" "" { target c } } */ +/* { dg-error "invalid operands of types 'float' and 'int' to binary 'operator\\^'" "" { target c++ } .-1 } */ + +/* Don't complain if the LHS isn't literal decimal 2 or 10. */ +int t1p1_16 = (1 + 1) ^ 16; +int t5p5_6 = (5 + 5) ^ 6; +int h2_8 = 0x2 ^ 8; +int h10_3 = 0xa ^ 3; + +/* Don't complain if the RHS isn't literal decimal. */ +int t2_x16 = 2^0x10; +int h10_x3 = 10 ^ 0x3; diff --git a/gcc/testsuite/c-c++-common/Wxor-used-as-pow-fixits.c b/gcc/testsuite/c-c++-common/Wxor-used-as-pow-fixits.c new file mode 100644 index 00000000000..f612b761c30 --- /dev/null +++ b/gcc/testsuite/c-c++-common/Wxor-used-as-pow-fixits.c @@ -0,0 +1,34 @@ +/* { dg-options "-fdiagnostics-show-caret" } */ + +/* Test fixit hints for -Wxor-used-as-pow. */ + +int t2_8 = 2^8; /* { dg-line line_a } */ +/* { dg-warning "result of '2\\^8' is 10; did you mean '1 << 8' \\(256\\)\\?" "warn" { target *-*-* } line_a } */ +/* { dg-begin-multiline-output "" } + int t2_8 = 2^8; + ^ + -- + 1<< + { dg-end-multiline-output "" } */ +/* { dg-message "you can silence this warning by using a hexadecimal constant \\(0x2 rather than 2\\)" "note" { target *-*-* } line_a } */ +/* { dg-begin-multiline-output "" } + int t2_8 = 2^8; + ^ + 0x2 + { dg-end-multiline-output "" } */ + + +int t10_6 = 10^6; /* { dg-line line_b } */ +/* { dg-warning "result of '10\\^6' is 12; did you mean '1e6'\\?" "warn" { target *-*-* } line_b } */ +/* { dg-begin-multiline-output "" } + int t10_6 = 10^6; + ^ + --- + 1e + { dg-end-multiline-output "" } */ +/* { dg-message "you can silence this warning by using a hexadecimal constant \\(0xa rather than 10\\)" "note" { target *-*-* } line_b } */ +/* { dg-begin-multiline-output "" } + int t10_6 = 10^6; + ^~ + 0xa + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/g++.dg/parse/expr3.C b/gcc/testsuite/g++.dg/parse/expr3.C index 95d332f41b0..1e166733843 100644 --- a/gcc/testsuite/g++.dg/parse/expr3.C +++ b/gcc/testsuite/g++.dg/parse/expr3.C @@ -23,7 +23,7 @@ test (||, &&, 1, 1, 0, 0) test (&&, |, 5, 1, 1, 19) -test (|, ^, 1, 2, 2, 1) +test (|, ^, 1, 2, 0x2, 1) test (^, &, 1, 3, 2, 6) test (&, ==, 1, 3, 2, 0) test (==, <, 2, 0, 0, 0) diff --git a/gcc/testsuite/g++.dg/warn/Wparentheses-10.C b/gcc/testsuite/g++.dg/warn/Wparentheses-10.C index 557db091ad0..a5d28efcc2e 100644 --- a/gcc/testsuite/g++.dg/warn/Wparentheses-10.C +++ b/gcc/testsuite/g++.dg/warn/Wparentheses-10.C @@ -16,7 +16,7 @@ bar (int a, int b, int c) foo (1 & (2 ^ c)); foo (1 & 2 ^ 3); // { dg-warning "parentheses" "correct warning" } foo ((1 & 2) ^ 3); - foo (1 & (2 ^ 3)); + foo (1 & (0x2 ^ 3)); foo (a ^ b & c); // { dg-warning "parentheses" "correct warning" } foo ((a ^ b) & c); foo (a ^ (b & c)); @@ -34,7 +34,7 @@ bar (int a, int b, int c) foo (1 + (2 ^ c)); foo (1 + 2 ^ 3); // { dg-warning "parentheses" "correct warning" } foo ((1 + 2) ^ 3); - foo (1 + (2 ^ 3)); + foo (1 + (0x2 ^ 3)); foo (a ^ b + c); // { dg-warning "parentheses" "correct warning" } foo ((a ^ b) + c); foo (a ^ (b + c)); @@ -52,7 +52,7 @@ bar (int a, int b, int c) foo (1 - (2 ^ c)); foo (1 - 2 ^ 3); // { dg-warning "parentheses" "correct warning" } foo ((1 - 2) ^ 3); - foo (1 - (2 ^ 3)); + foo (1 - (0x2 ^ 3)); foo (a ^ b - c); // { dg-warning "parentheses" "correct warning" } foo ((a ^ b) - c); foo (a ^ (b - c)); @@ -70,7 +70,7 @@ bar (int a, int b, int c) foo (1 >= (2 ^ c)); foo (1 >= 2 ^ 3); // { dg-warning "parentheses" "correct warning" } foo ((1 >= 2) ^ 3); - foo (1 >= (2 ^ 3)); + foo (1 >= (0x2 ^ 3)); foo (a ^ b >= c); // { dg-warning "parentheses" "correct warning" } foo ((a ^ b) >= c); foo (a ^ (b >= c)); @@ -88,7 +88,7 @@ bar (int a, int b, int c) foo (1 == (2 ^ c)); foo (1 == 2 ^ 3); // { dg-warning "parentheses" "correct warning" } foo ((1 == 2) ^ 3); - foo (1 == (2 ^ 3)); + foo (1 == (0x2 ^ 3)); foo (a ^ b == c); // { dg-warning "parentheses" "correct warning" } foo ((a ^ b) == c); foo (a ^ (b == c)); @@ -104,9 +104,9 @@ bar (int a, int b, int c) foo (1 < 2 ^ c); // { dg-warning "parentheses" "correct warning" } foo ((1 < 2) ^ c); foo (1 < (2 ^ c)); - foo (1 < 2 ^ 3); // { dg-warning "parentheses" "correct warning" } + foo (1 < 0x2 ^ 3); // { dg-warning "parentheses" "correct warning" } foo ((1 < 2) ^ 3); - foo (1 < (2 ^ 3)); + foo (1 < (0x2 ^ 3)); foo (a ^ b < c); // { dg-warning "parentheses" "correct warning" } foo ((a ^ b) < c); foo (a ^ (b < c)); diff --git a/gcc/testsuite/g++.dg/warn/Wparentheses-18.C b/gcc/testsuite/g++.dg/warn/Wparentheses-18.C index 83efaff4189..d50fad8ae80 100644 --- a/gcc/testsuite/g++.dg/warn/Wparentheses-18.C +++ b/gcc/testsuite/g++.dg/warn/Wparentheses-18.C @@ -42,9 +42,9 @@ bar (T a, T b, T c) foo (1 | 2 ^ c); // { dg-warning "parentheses" "correct warning" } foo ((1 | 2) ^ c); foo (1 | (2 ^ c)); - foo (1 | 2 ^ 3); // { dg-warning "parentheses" "correct warning" } + foo (1 | 0x2 ^ 3); // { dg-warning "parentheses" "correct warning" } foo ((1 | 2) ^ 3); - foo (1 | (2 ^ 3)); + foo (1 | (0x2 ^ 3)); foo (a + b | c); // { dg-warning "parentheses" "correct warning" } foo ((a + b) | c); foo (a + (b | c)); diff --git a/gcc/testsuite/g++.dg/warn/Wparentheses-19.C b/gcc/testsuite/g++.dg/warn/Wparentheses-19.C index f0e2b805c9e..2ad8036ac84 100644 --- a/gcc/testsuite/g++.dg/warn/Wparentheses-19.C +++ b/gcc/testsuite/g++.dg/warn/Wparentheses-19.C @@ -17,7 +17,7 @@ bar (T a, T b, T c) foo (1 & (2 ^ c)); foo (1 & 2 ^ 3); // { dg-warning "parentheses" "correct warning" } foo ((1 & 2) ^ 3); - foo (1 & (2 ^ 3)); + foo (1 & (0x2 ^ 3)); foo (a ^ b & c); // { dg-warning "parentheses" "correct warning" } foo ((a ^ b) & c); foo (a ^ (b & c)); @@ -35,7 +35,7 @@ bar (T a, T b, T c) foo (1 + (2 ^ c)); foo (1 + 2 ^ 3); // { dg-warning "parentheses" "correct warning" } foo ((1 + 2) ^ 3); - foo (1 + (2 ^ 3)); + foo (1 + (0x2 ^ 3)); foo (a ^ b + c); // { dg-warning "parentheses" "correct warning" } foo ((a ^ b) + c); foo (a ^ (b + c)); @@ -53,7 +53,7 @@ bar (T a, T b, T c) foo (1 - (2 ^ c)); foo (1 - 2 ^ 3); // { dg-warning "parentheses" "correct warning" } foo ((1 - 2) ^ 3); - foo (1 - (2 ^ 3)); + foo (1 - (0x2 ^ 3)); foo (a ^ b - c); // { dg-warning "parentheses" "correct warning" } foo ((a ^ b) - c); foo (a ^ (b - c)); @@ -71,7 +71,7 @@ bar (T a, T b, T c) foo (1 >= (2 ^ c)); foo (1 >= 2 ^ 3); // { dg-warning "parentheses" "correct warning" } foo ((1 >= 2) ^ 3); - foo (1 >= (2 ^ 3)); + foo (1 >= (0x2 ^ 3)); foo (a ^ b >= c); // { dg-warning "parentheses" "correct warning" } foo ((a ^ b) >= c); foo (a ^ (b >= c)); @@ -89,7 +89,7 @@ bar (T a, T b, T c) foo (1 == (2 ^ c)); foo (1 == 2 ^ 3); // { dg-warning "parentheses" "correct warning" } foo ((1 == 2) ^ 3); - foo (1 == (2 ^ 3)); + foo (1 == (0x2 ^ 3)); foo (a ^ b == c); // { dg-warning "parentheses" "correct warning" } foo ((a ^ b) == c); foo (a ^ (b == c)); @@ -107,7 +107,7 @@ bar (T a, T b, T c) foo (1 < (2 ^ c)); foo (1 < 2 ^ 3); // { dg-warning "parentheses" "correct warning" } foo ((1 < 2) ^ 3); - foo (1 < (2 ^ 3)); + foo (1 < (0x2 ^ 3)); foo (a ^ b < c); // { dg-warning "parentheses" "correct warning" } foo ((a ^ b) < c); foo (a ^ (b < c)); diff --git a/gcc/testsuite/g++.dg/warn/Wparentheses-9.C b/gcc/testsuite/g++.dg/warn/Wparentheses-9.C index 7c8f01d327b..69976b3bacf 100644 --- a/gcc/testsuite/g++.dg/warn/Wparentheses-9.C +++ b/gcc/testsuite/g++.dg/warn/Wparentheses-9.C @@ -41,9 +41,9 @@ bar (int a, int b, int c) foo (1 | 2 ^ c); // { dg-warning "parentheses" "correct warning" } foo ((1 | 2) ^ c); foo (1 | (2 ^ c)); - foo (1 | 2 ^ 3); // { dg-warning "parentheses" "correct warning" } + foo (1 | 0x2 ^ 3); // { dg-warning "parentheses" "correct warning" } foo ((1 | 2) ^ 3); - foo (1 | (2 ^ 3)); + foo (1 | (0x2 ^ 3)); foo (a + b | c); // { dg-warning "parentheses" "correct warning" } foo ((a + b) | c); foo (a + (b | c)); diff --git a/gcc/testsuite/g++.dg/warn/Wxor-used-as-pow-named-op.C b/gcc/testsuite/g++.dg/warn/Wxor-used-as-pow-named-op.C new file mode 100644 index 00000000000..4899d72b11b --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wxor-used-as-pow-named-op.C @@ -0,0 +1,8 @@ +/* Verify that -Wxor-used-as-pow only warns with ^ and not with + named "xor". */ + +int t2_16 = 2^16; /* { dg-warning "result of '2\\^16' is 18; did you mean '1 << 16' \\(65536\\)\\?" } */ +int t2x16 = 2 xor 16; + +int t10_6 = 10^6; /* { dg-warning "result of '10\\^6' is 12; did you mean '1e6'\\?" } */ +int t10x6 = 10 xor 6; diff --git a/gcc/testsuite/gcc.dg/Wparentheses-6.c b/gcc/testsuite/gcc.dg/Wparentheses-6.c index 2d2cc161394..260b6adebea 100644 --- a/gcc/testsuite/gcc.dg/Wparentheses-6.c +++ b/gcc/testsuite/gcc.dg/Wparentheses-6.c @@ -43,9 +43,9 @@ bar (int a, int b, int c) foo (1 | 2 ^ c); /* { dg-warning "parentheses" "correct warning" } */ foo ((1 | 2) ^ c); foo (1 | (2 ^ c)); - foo (1 | 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */ + foo (1 | 0x2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */ foo ((1 | 2) ^ 3); - foo (1 | (2 ^ 3)); + foo (1 | (0x2 ^ 3)); foo (a + b | c); /* { dg-warning "parentheses" "correct warning" } */ foo ((a + b) | c); foo (a + (b | c)); diff --git a/gcc/testsuite/gcc.dg/Wparentheses-7.c b/gcc/testsuite/gcc.dg/Wparentheses-7.c index f3516969d23..6805094d296 100644 --- a/gcc/testsuite/gcc.dg/Wparentheses-7.c +++ b/gcc/testsuite/gcc.dg/Wparentheses-7.c @@ -18,7 +18,7 @@ bar (int a, int b, int c) foo (1 & (2 ^ c)); foo (1 & 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */ foo ((1 & 2) ^ 3); - foo (1 & (2 ^ 3)); + foo (1 & (0x2 ^ 3)); foo (a ^ b & c); /* { dg-warning "parentheses" "correct warning" } */ foo ((a ^ b) & c); foo (a ^ (b & c)); @@ -36,7 +36,7 @@ bar (int a, int b, int c) foo (1 + (2 ^ c)); foo (1 + 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */ foo ((1 + 2) ^ 3); - foo (1 + (2 ^ 3)); + foo (1 + (0x2 ^ 3)); foo (a ^ b + c); /* { dg-warning "parentheses" "correct warning" } */ foo ((a ^ b) + c); foo (a ^ (b + c)); @@ -54,7 +54,7 @@ bar (int a, int b, int c) foo (1 - (2 ^ c)); foo (1 - 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */ foo ((1 - 2) ^ 3); - foo (1 - (2 ^ 3)); + foo (1 - (0x2 ^ 3)); foo (a ^ b - c); /* { dg-warning "parentheses" "correct warning" } */ foo ((a ^ b) - c); foo (a ^ (b - c)); @@ -72,7 +72,7 @@ bar (int a, int b, int c) foo (1 >= (2 ^ c)); foo (1 >= 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */ foo ((1 >= 2) ^ 3); - foo (1 >= (2 ^ 3)); + foo (1 >= (0x2 ^ 3)); foo (a ^ b >= c); /* { dg-warning "parentheses" "correct warning" } */ foo ((a ^ b) >= c); foo (a ^ (b >= c)); @@ -90,7 +90,7 @@ bar (int a, int b, int c) foo (1 == (2 ^ c)); foo (1 == 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */ foo ((1 == 2) ^ 3); - foo (1 == (2 ^ 3)); + foo (1 == (0x2 ^ 3)); foo (a ^ b == c); /* { dg-warning "parentheses" "correct warning" } */ foo ((a ^ b) == c); foo (a ^ (b == c)); @@ -108,7 +108,7 @@ bar (int a, int b, int c) foo (1 < (2 ^ c)); foo (1 < 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */ foo ((1 < 2) ^ 3); - foo (1 < (2 ^ 3)); + foo (1 < (0x2 ^ 3)); foo (a ^ b < c); /* { dg-warning "parentheses" "correct warning" } */ foo ((a ^ b) < c); foo (a ^ (b < c)); diff --git a/gcc/testsuite/gcc.dg/precedence-1.c b/gcc/testsuite/gcc.dg/precedence-1.c index f3f1e352708..089cdfc1472 100644 --- a/gcc/testsuite/gcc.dg/precedence-1.c +++ b/gcc/testsuite/gcc.dg/precedence-1.c @@ -135,10 +135,10 @@ f (void) ASSERT_BIN (0, !=, 2, &, 1, 1, 1, 0); ASSERT_BIN (1, &, 2, ==, 0, 0, 1, 0); ASSERT_BIN (1, &, 2, !=, 0, 1, 0, 1); - ASSERT_BIN (1, &, 2, ^, 3, 3, 3, 1); + ASSERT_BIN (1, &, 0x2, ^, 3, 3, 3, 1); ASSERT_BIN (3, ^, 2, &, 1, 3, 1, 3); ASSERT_BIN (3, ^, 2, |, 1, 1, 1, 0); - ASSERT_BIN (3, |, 2, ^, 1, 3, 2, 3); + ASSERT_BIN (3, |, 0x2, ^, 1, 3, 2, 3); ASSERT_BIN (2, |, 0, &&, 2, 1, 1, 2); ASSERT_BIN (2, &&, 0, |, 2, 1, 2, 1); ASSERT_BIN (0, &&, 0, ||, 1, 1, 1, 0); diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h index 3eba6f74b57..f1e7c2b3b2a 100644 --- a/libcpp/include/cpplib.h +++ b/libcpp/include/cpplib.h @@ -190,7 +190,7 @@ struct GTY(()) cpp_string { #define NAMED_OP (1 << 4) /* C++ named operators. */ #define PREV_FALLTHROUGH (1 << 5) /* On a token preceeded by FALLTHROUGH comment. */ -#define BOL (1 << 6) /* Token at beginning of line. */ +#define DECIMAL_INT (1 << 6) /* Decimal integer, set in c-lex.cc. */ #define PURE_ZERO (1 << 7) /* Single 0 digit, used by the C++ frontend, set in c-lex.cc. */ #define SP_DIGRAPH (1 << 8) /* # or ## token was a digraph. */ @@ -199,6 +199,7 @@ struct GTY(()) cpp_string { after a # operator. */ #define NO_EXPAND (1 << 10) /* Do not macro-expand this token. */ #define PRAGMA_OP (1 << 11) /* _Pragma token. */ +#define BOL (1 << 12) /* Token at beginning of line. */ /* Specify which field, if any, of the cpp_token union is used. */