c++: Implement -Wself-move warning [PR81159]

About 5 years ago we got a request to implement -Wself-move, which
warns about useless moves like this:

  int x;
  x = std::move (x);

This patch implements that warning.

	PR c++/81159

gcc/c-family/ChangeLog:

	* c.opt (Wself-move): New option.

gcc/cp/ChangeLog:

	* typeck.cc (maybe_warn_self_move): New.
	(cp_build_modify_expr): Call maybe_warn_self_move.

gcc/ChangeLog:

	* doc/invoke.texi: Document -Wself-move.

gcc/testsuite/ChangeLog:

	* g++.dg/warn/Wself-move1.C: New test.
This commit is contained in:
Marek Polacek 2022-08-08 17:45:28 -04:00
parent 1e2462890a
commit 0abb78dda0
4 changed files with 203 additions and 2 deletions

View File

@ -1229,6 +1229,10 @@ Wselector
ObjC ObjC++ Var(warn_selector) Warning
Warn if a selector has multiple methods.
Wself-move
C++ ObjC++ Var(warn_self_move) Warning LangEnabledBy(C++ ObjC++, Wall)
Warn when a value is moved to itself with std::move.
Wsequence-point
C ObjC C++ ObjC++ Var(warn_sequence_point) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
Warn about possible violations of sequence point rules.

View File

@ -8897,7 +8897,56 @@ cp_build_c_cast (location_t loc, tree type, tree expr,
return error_mark_node;
}
/* Warn when a value is moved to itself with std::move. LHS is the target,
RHS may be the std::move call, and LOC is the location of the whole
assignment. */
static void
maybe_warn_self_move (location_t loc, tree lhs, tree rhs)
{
if (!warn_self_move)
return;
/* C++98 doesn't know move. */
if (cxx_dialect < cxx11)
return;
if (processing_template_decl)
return;
if (!REFERENCE_REF_P (rhs)
|| TREE_CODE (TREE_OPERAND (rhs, 0)) != CALL_EXPR)
return;
tree fn = TREE_OPERAND (rhs, 0);
if (!is_std_move_p (fn))
return;
/* Just a little helper to strip * and various NOPs. */
auto extract_op = [] (tree &op) {
STRIP_NOPS (op);
while (INDIRECT_REF_P (op))
op = TREE_OPERAND (op, 0);
op = maybe_undo_parenthesized_ref (op);
STRIP_ANY_LOCATION_WRAPPER (op);
};
tree arg = CALL_EXPR_ARG (fn, 0);
extract_op (arg);
if (TREE_CODE (arg) == ADDR_EXPR)
arg = TREE_OPERAND (arg, 0);
tree type = TREE_TYPE (lhs);
tree orig_lhs = lhs;
extract_op (lhs);
if (cp_tree_equal (lhs, arg))
{
auto_diagnostic_group d;
if (warning_at (loc, OPT_Wself_move,
"moving %qE of type %qT to itself", orig_lhs, type))
inform (loc, "remove %<std::move%> call");
}
}
/* For use from the C common bits. */
tree
build_modify_expr (location_t location,
@ -9101,6 +9150,8 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
if (modifycode == NOP_EXPR)
{
maybe_warn_self_move (loc, lhs, rhs);
if (c_dialect_objc ())
{
result = objc_maybe_build_modify_expr (lhs, rhs);

View File

@ -264,7 +264,7 @@ in the following sections.
-Wreorder -Wregister @gol
-Wstrict-null-sentinel -Wno-subobject-linkage -Wtemplates @gol
-Wno-non-template-friend -Wold-style-cast @gol
-Woverloaded-virtual -Wno-pmf-conversions -Wsign-promo @gol
-Woverloaded-virtual -Wno-pmf-conversions -Wself-move -Wsign-promo @gol
-Wsized-deallocation -Wsuggest-final-methods @gol
-Wsuggest-final-types -Wsuggest-override @gol
-Wno-terminate -Wuseless-cast -Wno-vexing-parse @gol
@ -5838,6 +5838,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}.
-Wreorder @gol
-Wrestrict @gol
-Wreturn-type @gol
-Wself-move @r{(only for C++)} @gol
-Wsequence-point @gol
-Wsign-compare @r{(only in C++)} @gol
-Wsizeof-array-div @gol
@ -6823,6 +6824,26 @@ of a declaration:
This warning is enabled by @option{-Wall}.
@item -Wno-self-move @r{(C++ and Objective-C++ only)}
@opindex Wself-move
@opindex Wno-self-move
This warning warns when a value is moved to itself with @code{std::move}.
Such a @code{std::move} typically has no effect.
@smallexample
struct T @{
@dots{}
@};
void fn()
@{
T t;
@dots{}
t = std::move (t);
@}
@end smallexample
This warning is enabled by @option{-Wall}.
@item -Wsequence-point
@opindex Wsequence-point
@opindex Wno-sequence-point

View File

@ -0,0 +1,125 @@
// PR c++/81159
// { dg-do compile { target c++11 } }
// { dg-options "-Wself-move" }
// Define std::move.
namespace std {
template<typename _Tp>
struct remove_reference
{ typedef _Tp type; };
template<typename _Tp>
struct remove_reference<_Tp&>
{ typedef _Tp type; };
template<typename _Tp>
struct remove_reference<_Tp&&>
{ typedef _Tp type; };
template<typename _Tp>
constexpr typename std::remove_reference<_Tp>::type&&
move(_Tp&& __t) noexcept
{ return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
}
int g;
struct S {
int x;
S(S&& o) {
x = std::move (x); // { dg-warning "moving '\[^\n\r]*S::x' of type .int. to itself" }
x = std::move (o.x);
o.x = std::move (x);
o.x = std::move (o.x); // { dg-warning "moving 'o.S::x' of type .int. to itself" }
}
void foo (int x) {
x = std::move (x); // { dg-warning "moving 'x' of type .int. to itself" }
}
};
struct X {
int x;
X(int x) : x(std::move (x)) { }
};
struct A {};
struct B { A a; };
struct C { C(); ~C(); };
struct D { D(); D(const D&); D(D&&); D& operator=(const D&); };
void
test ()
{
int i = 42;
i = std::move (i); // { dg-warning "moving 'i' of type .int. to itself" }
(i) = std::move (i); // { dg-warning "moving 'i' of type .int. to itself" }
g = std::move (g); // { dg-warning "moving 'g' of type .int. to itself" }
(g) = std::move (g); // { dg-warning "moving 'g' of type .int. to itself" }
A a;
a = std::move (a); // { dg-warning "moving 'a' of type .A. to itself" }
(a) = std::move (a); // { dg-warning "moving 'a' of type .A. to itself" }
B b;
b = std::move (b); // { dg-warning "moving 'b' of type .B. to itself" }
(b) = std::move (b); // { dg-warning "moving 'b' of type .B. to itself" }
b.a = std::move (b.a); // { dg-warning "moving 'b.B::a' of type .A. to itself" }
(b.a) = std::move (b.a); // { dg-warning "moving 'b.B::a' of type .A. to itself" }
C c;
c = std::move (c); // { dg-warning "moving 'c' of type .C. to itself" }
D d;
d = std::move (d); // { dg-warning "moving 'd' of type .D. to itself" }
}
template<typename T>
void ttest ()
{
T t;
t = std::move (t); // { dg-warning "moving 't' of type .A. to itself" }
}
template void ttest<A>();
void
testref (int &r, int &&rr)
{
r = std::move (r); // { dg-warning "moving 'r' of type .int. to itself" }
rr = std::move (rr); // { dg-warning "moving 'rr' of type .int. to itself" }
}
// Test various other arguments to std::move.
template<typename T>
void
testargs (T *Tptr, T **Tpptr, T& Tref, T&& Trref, const T *Tcptr)
{
Tptr = std::move (Tptr); // { dg-warning "moving 'Tptr' of type 'int\\*' to itself" }
*Tptr = std::move (*Tptr); // { dg-warning "moving '\\* Tptr' of type 'int' to itself" }
*Tptr = std::move (*(Tptr)); // { dg-warning "moving '\\* Tptr' of type 'int' to itself" }
*(Tptr) = std::move (*Tptr); // { dg-warning "moving '\\* Tptr' of type 'int' to itself" }
*(Tptr + 1) = std::move (*(Tptr + 1)); // { dg-warning "moving '\[^\n\r]*Tptr\[^\n\r]*' of type 'int' to itself" }
*(Tptr + 1) = std::move (*(Tptr + 2));
(*(Tptr)) = std::move (*Tptr); // { dg-warning "moving '\\* Tptr' of type 'int' to itself" }
*Tpptr = std::move (*Tpptr); // { dg-warning "moving '\\* Tpptr' of type 'int\\*' to itself" }
**Tpptr = std::move (**Tpptr); // { dg-warning "moving '\\* \\* Tpptr' of type 'int' to itself" }
Tref = std::move (Tref); // { dg-warning "moving 'Tref' of type 'int' to itself" }
Trref = std::move (Trref); // { dg-warning "moving 'Trref' of type 'int' to itself" }
Tcptr = std::move (Tcptr); // { dg-warning "moving 'Tcptr' of type 'const int\\*' to itself" }
(Tptr) = std::move (Tptr); // { dg-warning "moving 'Tptr' of type 'int\\*' to itself" }
(*Tptr) = std::move (*Tptr); // { dg-warning "moving '\\* Tptr' of type 'int' to itself" }
(*Tpptr) = std::move (*Tpptr); // { dg-warning "moving '\\* Tpptr' of type 'int\\*' to itself" }
(**Tpptr) = std::move (**Tpptr); // { dg-warning "moving '\\* \\* Tpptr' of type 'int' to itself" }
(*(*(Tpptr))) = std::move (**Tpptr); // { dg-warning "moving '\\* \\* Tpptr' of type 'int' to itself" }
(Tref) = std::move (Tref); // { dg-warning "moving 'Tref' of type 'int' to itself" }
(Trref) = std::move (Trref); // { dg-warning "moving 'Trref' of type 'int' to itself" }
(Tcptr) = std::move (Tcptr); // { dg-warning "moving 'Tcptr' of type 'const int\\*' to itself" }
}
void
call_testargs ()
{
int i = 42;
int *p = &i;
testargs<int>(&i, &p, i, 42, &i);
}