if (!DestType->isRecordType())
return;
- const CXXConstructExpr *CCE =
- dyn_cast<CXXConstructExpr>(InitExpr->IgnoreParens());
- if (!CCE || CCE->getNumArgs() != 1)
- return;
+ unsigned DiagID = 0;
+ if (IsReturnStmt) {
+ const CXXConstructExpr *CCE =
+ dyn_cast<CXXConstructExpr>(InitExpr->IgnoreParens());
+ if (!CCE || CCE->getNumArgs() != 1)
+ return;
- if (!CCE->getConstructor()->isCopyOrMoveConstructor())
- return;
+ if (!CCE->getConstructor()->isCopyOrMoveConstructor())
+ return;
- InitExpr = CCE->getArg(0)->IgnoreImpCasts();
+ InitExpr = CCE->getArg(0)->IgnoreImpCasts();
+ }
// Find the std::move call and get the argument.
const CallExpr *CE = dyn_cast<CallExpr>(InitExpr->IgnoreParens());
if (!CE || !CE->isCallToStdMove())
return;
- const Expr *Arg = CE->getArg(0);
+ const Expr *Arg = CE->getArg(0)->IgnoreImplicit();
- unsigned DiagID = 0;
-
- if (!IsReturnStmt && !isa<MaterializeTemporaryExpr>(Arg))
- return;
-
- if (isa<MaterializeTemporaryExpr>(Arg)) {
- DiagID = diag::warn_pessimizing_move_on_initialization;
- const Expr *ArgStripped = Arg->IgnoreImplicit()->IgnoreParens();
- if (!ArgStripped->isRValue() || !ArgStripped->getType()->isRecordType())
- return;
- } else { // IsReturnStmt
+ if (IsReturnStmt) {
const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Arg->IgnoreParenImpCasts());
if (!DRE || DRE->refersToEnclosingVariableOrCapture())
return;
DiagID = diag::warn_redundant_move_on_return;
else
DiagID = diag::warn_pessimizing_move_on_return;
+ } else {
+ DiagID = diag::warn_pessimizing_move_on_initialization;
+ const Expr *ArgStripped = Arg->IgnoreImplicit()->IgnoreParens();
+ if (!ArgStripped->isRValue() || !ArgStripped->getType()->isRecordType())
+ return;
}
S.Diag(CE->getBeginLoc(), DiagID);
// Get all the locations for a fix-it. Don't emit the fix-it if any location
// is within a macro.
- SourceLocation BeginLoc = CCE->getBeginLoc();
- if (BeginLoc.isMacroID())
+ SourceLocation CallBegin = CE->getCallee()->getBeginLoc();
+ if (CallBegin.isMacroID())
return;
SourceLocation RParen = CE->getRParenLoc();
if (RParen.isMacroID())
return;
+ SourceLocation LParen;
SourceLocation ArgLoc = Arg->getBeginLoc();
// Special testing for the argument location. Since the fix-it needs the
ArgLoc = S.getSourceManager().getImmediateExpansionRange(ArgLoc).getBegin();
}
- SourceLocation LParen = ArgLoc.getLocWithOffset(-1);
if (LParen.isMacroID())
return;
- SourceLocation EndLoc = CCE->getEndLoc();
- if (EndLoc.isMacroID())
- return;
+
+ LParen = ArgLoc.getLocWithOffset(-1);
S.Diag(CE->getBeginLoc(), diag::note_remove_move)
- << FixItHint::CreateRemoval(SourceRange(BeginLoc, LParen))
- << FixItHint::CreateRemoval(SourceRange(RParen, EndLoc));
+ << FixItHint::CreateRemoval(SourceRange(CallBegin, LParen))
+ << FixItHint::CreateRemoval(SourceRange(RParen, RParen));
}
static void CheckForNullPointerDereference(Sema &S, const Expr *E) {
// RUN: %clang_cc1 -fsyntax-only -Wpessimizing-move -std=c++11 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wpessimizing-move -std=c++11 -verify %s -DUSER_DEFINED
// RUN: %clang_cc1 -fsyntax-only -Wpessimizing-move -std=c++11 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// definitions for std::move
}
}
-struct A {};
+struct A {
+#ifdef USER_DEFINED
+ A() {}
+ A(const A &) {}
+ A(A &&) {}
+ A &operator=(const A &) { return *this; }
+ A &operator=(A &&) { return *this; }
+#endif
+};
struct B {
B() {}
B(A) {}
// expected-note@-2{{remove std::move call}}
// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:20}:""
// CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:22-[[@LINE-4]]:23}:""
+
+ return A();
+ return test1(a2);
+ return std::move(A());
+ // expected-warning@-1{{prevents copy elision}}
+ // expected-note@-2{{remove std::move call}}
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:20}:""
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:23-[[@LINE-4]]:24}:""
+ return std::move(test1(a2));
+ // expected-warning@-1{{prevents copy elision}}
+ // expected-note@-2{{remove std::move call}}
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:20}:""
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:29-[[@LINE-4]]:30}:""
}
A global_a;
// expected-note@-2{{remove std::move call}}
// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:20}:""
// CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:23-[[@LINE-4]]:24}:""
+
+ a3 = std::move(A());
+ // expected-warning@-1{{prevents copy elision}}
+ // expected-note@-2{{remove std::move call}}
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:8-[[@LINE-3]]:18}:""
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:21-[[@LINE-4]]:22}:""
+
A a4 = std::move(test3());
// expected-warning@-1{{prevents copy elision}}
// expected-note@-2{{remove std::move call}}
// CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:20}:""
// CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:27-[[@LINE-4]]:28}:""
+
+ a4 = std::move(test3());
+ // expected-warning@-1{{prevents copy elision}}
+ // expected-note@-2{{remove std::move call}}
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:8-[[@LINE-3]]:18}:""
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:25-[[@LINE-4]]:26}:""
}
A test7() {
A a3 = (std::move(A()));
// expected-warning@-1{{prevents copy elision}}
// expected-note@-2{{remove std::move call}}
- // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:21}:""
- // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:24-[[@LINE-4]]:26}:""
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:11-[[@LINE-3]]:21}:""
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:24-[[@LINE-4]]:25}:""
A a4 = (std::move((A())));
// expected-warning@-1{{prevents copy elision}}
// expected-note@-2{{remove std::move call}}
- // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:21}:""
- // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:26-[[@LINE-4]]:28}:""
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:11-[[@LINE-3]]:21}:""
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:26-[[@LINE-4]]:27}:""
return std::move(a1);
// expected-warning@-1{{prevents copy elision}}
return (std::move(a1));
// expected-warning@-1{{prevents copy elision}}
// expected-note@-2{{remove std::move call}}
- // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:21}:""
- // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:23-[[@LINE-4]]:25}:""
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:11-[[@LINE-3]]:21}:""
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:23-[[@LINE-4]]:24}:""
return (std::move((a1)));
// expected-warning@-1{{prevents copy elision}}
// expected-note@-2{{remove std::move call}}
- // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:21}:""
- // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:25-[[@LINE-4]]:27}:""
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:11-[[@LINE-3]]:21}:""
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:25-[[@LINE-4]]:26}:""
}
#define wrap1(x) x
test2<B, A>();
}
}
-
-A init_list() {
- A a1;
- return { std::move(a1) };
- // expected-warning@-1{{prevents copy elision}}
- // expected-note@-2{{remove std::move call}}
- // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:22}:""
- // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:24-[[@LINE-4]]:27}:""
-
- return { (std::move(a1)) };
- // expected-warning@-1{{prevents copy elision}}
- // expected-note@-2{{remove std::move call}}
- // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:23}:""
- // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:25-[[@LINE-4]]:29}:""
-
- A a2 = { std::move(A()) };
- // expected-warning@-1{{prevents copy elision}}
- // expected-note@-2{{remove std::move call}}
- // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:22}:""
- // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:25-[[@LINE-4]]:28}:""
-
- A a3 = { (std::move(A())) };
- // expected-warning@-1{{prevents copy elision}}
- // expected-note@-2{{remove std::move call}}
- // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:10-[[@LINE-3]]:23}:""
- // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:26-[[@LINE-4]]:30}:""
-}