mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-08 20:52:05 +08:00
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:
parent
1e2462890a
commit
0abb78dda0
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
125
gcc/testsuite/g++.dg/warn/Wself-move1.C
Normal file
125
gcc/testsuite/g++.dg/warn/Wself-move1.C
Normal 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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user