Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -307,6 +307,7 @@ def SelfAssignmentField : DiagGroup<"self-assign-field">; def SelfAssignment : DiagGroup<"self-assign", [SelfAssignmentField]>; def SelfMove : DiagGroup<"self-move">; +def UseOfMovedParameter : DiagGroup<"use-of-moved-parameter">; def SemiBeforeMethodBody : DiagGroup<"semicolon-before-method-body">; def Sentinel : DiagGroup<"sentinel">; def MissingMethodReturnType : DiagGroup<"missing-method-return-type">; @@ -580,7 +581,8 @@ def IntToPointerCast : DiagGroup<"int-to-pointer-cast", [IntToVoidPointerCast]>; -def Move : DiagGroup<"move", [PessimizingMove, RedundantMove, SelfMove]>; +def Move : DiagGroup<"move", [PessimizingMove, RedundantMove, SelfMove, + UseOfMovedParameter]>; def Extra : DiagGroup<"extra", [ MissingFieldInitializers, Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -4794,6 +4794,10 @@ "moving a temporary object prevents copy elision">, InGroup, DefaultIgnore; def note_remove_move : Note<"remove std::move call here">; +def warn_move_from_param_to_member : Warning< + "parameter '%0' owns nullptr, because it has been moved into member " + "'%0' ; did you mean 'this->%0'?">, InGroup; +def note_moved_from_here : Note<"parameter '%0' moved into member '%0' here">; def warn_string_plus_int : Warning< "adding %0 to a string does not append to the string">, Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -10602,6 +10602,114 @@ return ActOnFinishFunctionBody(D, BodyArg, false); } +namespace { +class FindUsesOfParamsMovedFrom + : public EvaluatedExprVisitor { + const ParmVarDecl *Param; + llvm::SmallVector &UseRanges; + using Inherited = EvaluatedExprVisitor; + +public: + FindUsesOfParamsMovedFrom(ASTContext &Context, const ParmVarDecl *Param, + llvm::SmallVector &UseRanges) + : Inherited(Context), Param(Param), UseRanges(UseRanges) {} + void VisitCallExpr(CallExpr *CE) { + if (CXXOperatorCallExpr *OCE = dyn_cast(CE)) + // We care about operator-> of smart pointers + // Check that thr first arg is Param! + if (OCE->getOperator() != OO_Arrow) { + return; + } + for (auto *Arg : CE->arguments()) { + if (DeclRefExpr *DRE = dyn_cast(Arg->IgnoreCasts())) + if (dyn_cast(DRE->getDecl()) == Param) { + UseRanges.push_back(CE->getSourceRange()); + return; + } + } + Inherited::VisitCallExpr(CE); + } +}; + +class FieldInitMovedFromParamVisitor + : public EvaluatedExprVisitor { + ParmVarDecl *Param; + StringRef FieldName; + static bool IsCXX11MovableSmartPtrType(QualType T) { + CXXRecordDecl *RD = T->getAsCXXRecordDecl(); + if (!RD) + return false; + if (!RD->isInStdNamespace()) + return false; + + StringRef Name = RD->getName(); + return Name == "shared_ptr" || Name == "unique_ptr"; + } + bool CheckMovedFromParam(Expr *E) { + if (DeclRefExpr *DRE = dyn_cast(E)) { + if (ParmVarDecl *PVD = dyn_cast(DRE->getDecl())) { + if (PVD->getName() == FieldName && + IsCXX11MovableSmartPtrType(PVD->getType())) { + Param = PVD; + return true; + } + } + } + return false; + } + +public: + typedef EvaluatedExprVisitor Inherited; + FieldInitMovedFromParamVisitor(ASTContext &Context, StringRef FieldName) + : Inherited(Context), Param(nullptr), FieldName(FieldName) {} + void VisitCallExpr(CallExpr *E) { + if (E->getNumArgs() == 1) { + if (FunctionDecl *FD = E->getDirectCallee()) { + if (FD->isInStdNamespace() && FD->getIdentifier() && + FD->getIdentifier()->isStr("move") && + CheckMovedFromParam(E->getArg(0))) + return; + } + } + Inherited::VisitCallExpr(E); + } + ParmVarDecl *getParam() { return Param; } +}; +} + +static void DiagnoseUseOfMovedParams(Sema &SemaRef, const FunctionDecl *FD) { + // FIXME: This is limited to ctors for now + const CXXConstructorDecl *CD = dyn_cast(FD); + if (!CD) + return; + llvm::SmallVector, 4> ParamsAndMoveLoc; + for (const auto *FieldInit : CD->inits()) { + FieldDecl *FD = FieldInit->getAnyMember(); + if (!FD) + continue; + Expr *E = FieldInit->getInit(); + if (!E) + continue; + FieldInitMovedFromParamVisitor V(FD->getASTContext(), FD->getName()); + V.Visit(E); + if (ParmVarDecl *P = V.getParam()) + ParamsAndMoveLoc.push_back(std::make_pair(P, E->getSourceRange())); + } + for (const auto &Pair : ParamsAndMoveLoc) { + llvm::SmallVector UseRanges; + FindUsesOfParamsMovedFrom Vis(CD->getASTContext(), Pair.first, UseRanges); + Vis.Visit(CD->getBody()); + for (const SourceRange &UseRange : UseRanges) { + SourceLocation Loc = UseRange.getBegin(); + StringRef Name = Pair.first->getName(); + SemaRef.Diag(Loc, diag::warn_move_from_param_to_member) + << Name << UseRange << FixItHint::CreateInsertion(Loc, "this->"); + SemaRef.Diag(Pair.second.getBegin(), diag::note_moved_from_here) + << Name << Pair.second; + } + } +} + Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, bool IsInstantiation) { FunctionDecl *FD = dcl ? dcl->getAsFunction() : nullptr; @@ -10670,8 +10778,11 @@ if (!FD->isInvalidDecl()) { // Don't diagnose unused parameters of defaulted or deleted functions. - if (!FD->isDeleted() && !FD->isDefaulted()) + if (!FD->isDeleted() && !FD->isDefaulted()) { DiagnoseUnusedParameters(FD->param_begin(), FD->param_end()); + if (getLangOpts().CPlusPlus11) + DiagnoseUseOfMovedParams(*this, FD); + } DiagnoseSizeOfParametersAndReturnValue(FD->param_begin(), FD->param_end(), FD->getReturnType(), FD); Index: test/SemaCXX/rval-references.cpp =================================================================== --- test/SemaCXX/rval-references.cpp +++ test/SemaCXX/rval-references.cpp @@ -92,3 +92,82 @@ else // Construction from different type can't be elided return i; // expected-error {{no viable conversion from 'int' to 'MoveOnly'}} } + +namespace std { +template +struct remove_reference { typedef T type; }; +template +struct remove_reference { typedef T type; }; +template +struct remove_reference { typedef T type; }; + +template +typename remove_reference::type &&move(T &&arg) { + return static_cast::type &&>(arg); +} + +template +class unique_ptr { + T *p; + +public: + unique_ptr() : p(nullptr) {} + unique_ptr(T *p) : p(std::move(p)) {} + unique_ptr(unique_ptr &&other) : p(other.release()) {} + T *release() { + T *tmp = p; + p = nullptr; + return tmp; + } + T *get() const { return p; } + T *operator->() const { return get(); } +}; +} + +namespace ParmMovedFromWarning { +struct X { + int v; + X() : v(0) {} + int f() { + v += 42; + return v; + } +}; + +int f(std::unique_ptr p) { + return p->f(); +} + +template +T g(T v) { + return v + 42; +} + +struct Y { + std::unique_ptr p; + explicit Y(std::unique_ptr p); +}; + +struct YBraceInit { + std::unique_ptr p; + explicit YBraceInit(std::unique_ptr p) + : p{std::move(p)} { } +}; + +Y::Y(std::unique_ptr p) + : p(std::move(p)) { // expected-note 3{{here}} + + p->f(); // expected-warning {{moved into member}} + f(std::move(p)); // expected-warning {{moved into member}} + p->v = 42; // expected-warning {{moved into member}} + gf())>(decltype(p->v){}); + decltype(p->f()) k = 0; + decltype(f(std::move(p))) j = 0; +} + +void gain() { + std::unique_ptr p(new X); + Y y(std::move(p)); +} +} +