Skip to content

Commit db63152

Browse files
committed
Reject chained assignment, don't emit [[nodiscard]] on = and ?= (See #368 comments)
1 parent b50446a commit db63152

File tree

6 files changed

+72
-10
lines changed

6 files changed

+72
-10
lines changed

Diff for: regression-tests/pure2-bugfix-for-discard-precedence.cpp2

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ quantity: type = {
55
_ = _;
66
}
77
operator+=: (inout this, that) -> forward quantity = {
8-
_ = number += that.number;
8+
number += that.number;
99
return this;
1010
}
1111
}
1212

1313
main: () = {
1414
x: quantity = (std::in_place, 1729);
15-
_ = x += x;
15+
x += x;
1616
}

Diff for: regression-tests/test-results/pure2-bugfix-for-discard-precedence.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class quantity {
1919

2020

2121
#line 7 "pure2-bugfix-for-discard-precedence.cpp2"
22-
public: [[nodiscard]] auto operator+=(quantity const& that) -> quantity&;
22+
public: auto operator+=(quantity const& that) -> quantity&;
2323

2424
public: quantity(quantity const&) = delete; /* No 'that' constructor, suppress copy */
2525
public: auto operator=(quantity const&) -> void = delete;
@@ -42,14 +42,14 @@ auto main() -> int;
4242

4343
static_cast<void>(_);
4444
}
45-
[[nodiscard]] auto quantity::operator+=(quantity const& that) -> quantity&{
46-
static_cast<void>(number += that.number);
45+
auto quantity::operator+=(quantity const& that) -> quantity&{
46+
number += that.number;
4747
return (*this);
4848
}
4949

5050
#line 13 "pure2-bugfix-for-discard-precedence.cpp2"
5151
auto main() -> int{
5252
quantity x {std::in_place, 1729};
53-
static_cast<void>(x += std::move(x));
53+
x += std::move(x);
5454
}
5555

Diff for: regression-tests/test-results/version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
cppfront compiler v0.2.1 Build 8810:1840
2+
cppfront compiler v0.2.1 Build 8811:0910
33
Copyright(c) Herb Sutter All rights reserved
44

55
SPDX-License-Identifier: CC-BY-NC-ND-4.0

Diff for: source/build.info

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
"8810:1840"
1+
"8811:0910"

Diff for: source/cppfront.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -5459,6 +5459,8 @@ class cppfront
54595459
// -- for now there's no opt-out, wait and see whether we actually need one
54605460
if (
54615461
func->has_non_void_return_type()
5462+
&& !func->is_assignment()
5463+
&& !func->is_compound_assignment()
54625464
&& (
54635465
printer.get_phase() == printer.phase1_type_defs_func_decls
54645466
|| n.has_initializer() // so we're printing it in phase 2

Diff for: source/parse.h

+62-2
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,12 @@ struct binary_expression_node
280280

281281
// API
282282
//
283+
auto terms_size() const
284+
-> int
285+
{
286+
return std::ssize(terms);
287+
}
288+
283289
auto is_identifier() const
284290
-> bool
285291
{
@@ -1971,6 +1977,9 @@ struct function_type_node
19711977
auto is_constructor_with_move_that() const
19721978
-> bool;
19731979

1980+
auto is_compound_assignment() const
1981+
-> bool;
1982+
19741983
auto is_assignment() const
19751984
-> bool;
19761985

@@ -2844,6 +2853,16 @@ struct declaration_node
28442853
return false;
28452854
}
28462855

2856+
auto is_compound_assignment() const
2857+
-> bool
2858+
{
2859+
if (auto func = std::get_if<a_function>(&type)) {
2860+
return (*func)->is_compound_assignment();
2861+
}
2862+
// else
2863+
return false;
2864+
}
2865+
28472866
auto is_assignment() const
28482867
-> bool
28492868
{
@@ -3298,6 +3317,33 @@ auto function_type_node::is_constructor_with_move_that() const
32983317
}
32993318

33003319

3320+
auto function_type_node::is_compound_assignment() const
3321+
-> bool
3322+
{
3323+
if (
3324+
(
3325+
my_decl->has_name("operator+=")
3326+
|| my_decl->has_name("operator-=")
3327+
|| my_decl->has_name("operator*=")
3328+
|| my_decl->has_name("operator/=")
3329+
|| my_decl->has_name("operator%=")
3330+
|| my_decl->has_name("operator&=")
3331+
|| my_decl->has_name("operator|=")
3332+
|| my_decl->has_name("operator^=")
3333+
|| my_decl->has_name("operator<<=")
3334+
|| my_decl->has_name("operator>>=")
3335+
)
3336+
&& (*parameters).ssize() > 1
3337+
&& (*parameters)[0]->has_name("this")
3338+
&& (*parameters)[0]->direction() == passing_style::inout
3339+
)
3340+
{
3341+
return true;
3342+
}
3343+
return false;
3344+
}
3345+
3346+
33013347
auto function_type_node::is_assignment() const
33023348
-> bool
33033349
{
@@ -4555,7 +4601,7 @@ class parser
45554601
{
45564602
if (allow_angle_operators)
45574603
{
4558-
return binary_expression<assignment_expression_node> (
4604+
auto ret = binary_expression<assignment_expression_node> (
45594605
[this](token const& t, token const& next) -> token const* {
45604606
if (is_assignment_operator(t.type())) {
45614607
return &t;
@@ -4573,13 +4619,27 @@ class parser
45734619
},
45744620
[=,this]{ return logical_or_expression(allow_angle_operators); }
45754621
);
4622+
4623+
if (ret && ret->terms_size() > 1) {
4624+
error("assignment cannot be chained - instead of 'c = b = a;', write 'b = a; c = b;'");
4625+
return {};
4626+
}
4627+
4628+
return ret;
45764629
}
45774630
else
45784631
{
4579-
return binary_expression<assignment_expression_node> (
4632+
auto ret = binary_expression<assignment_expression_node> (
45804633
[](token const&, token const&) -> token const* { return nullptr; },
45814634
[=,this]{ return logical_or_expression(allow_angle_operators); }
45824635
);
4636+
4637+
if (ret && ret->terms_size() > 1) {
4638+
error("assignment cannot be chained - instead of 'c = b = a;', write 'b = a; c = b;'");
4639+
return {};
4640+
}
4641+
4642+
return ret;
45834643
}
45844644
}
45854645

0 commit comments

Comments
 (0)