[clang-tools-extra] r251788 - modernize-use-default supports copy constructor and copy-assignment operator.
Angel Garcia Gomez via cfe-commits
cfe-commits at lists.llvm.org
Mon Nov 2 02:34:19 PST 2015
Author: angelgarcia
Date: Mon Nov 2 04:34:19 2015
New Revision: 251788
URL: http://llvm.org/viewvc/llvm-project?rev=251788&view=rev
Log:
modernize-use-default supports copy constructor and copy-assignment operator.
Summary: the check will now warn when the user provided definitions of this functions is equivalent to the explicitly defaulted ones.
Reviewers: klimek
Subscribers: klimek, cfe-commits, alexfh
Differential Revision: http://reviews.llvm.org/D14145
Added:
clang-tools-extra/trunk/test/clang-tidy/modernize-use-default-copy.cpp
Modified:
clang-tools-extra/trunk/clang-tidy/modernize/UseDefaultCheck.cpp
clang-tools-extra/trunk/test/clang-tidy/modernize-use-default.cpp
Modified: clang-tools-extra/trunk/clang-tidy/modernize/UseDefaultCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/modernize/UseDefaultCheck.cpp?rev=251788&r1=251787&r2=251788&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/modernize/UseDefaultCheck.cpp (original)
+++ clang-tools-extra/trunk/clang-tidy/modernize/UseDefaultCheck.cpp Mon Nov 2 04:34:19 2015
@@ -10,6 +10,7 @@
#include "UseDefaultCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
@@ -17,45 +18,302 @@ namespace clang {
namespace tidy {
namespace modernize {
-static const char CtorDtor[] = "CtorDtorDecl";
+static const char SpecialFunction[] = "SpecialFunction";
+
+/// \brief Finds the SourceLocation of the colon ':' before the initialization
+/// list in the definition of a constructor.
+static SourceLocation getColonLoc(const ASTContext *Context,
+ const CXXConstructorDecl *Ctor) {
+ // FIXME: First init is the first initialization that is going to be
+ // performed, no matter what was the real order in the source code. If the
+ // order of the inits is wrong in the code, it may result in a false negative.
+ SourceLocation FirstInit = (*Ctor->init_begin())->getSourceLocation();
+ SourceLocation LastArg =
+ Ctor->getParamDecl(Ctor->getNumParams() - 1)->getLocEnd();
+ // We need to find the colon between the ')' and the first initializer.
+ bool Invalid = false;
+ StringRef Text = Lexer::getSourceText(
+ CharSourceRange::getCharRange(LastArg, FirstInit),
+ Context->getSourceManager(), Context->getLangOpts(), &Invalid);
+ if (Invalid)
+ return SourceLocation();
+
+ size_t ColonPos = Text.rfind(':');
+ if (ColonPos == StringRef::npos)
+ return SourceLocation();
+
+ Text = Text.drop_front(ColonPos + 1);
+ if (std::strspn(Text.data(), " \t\r\n") != Text.size()) {
+ // If there are comments, preprocessor directives or anything, abort.
+ return SourceLocation();
+ }
+ // FIXME: don't remove comments in the middle of the initializers.
+ return LastArg.getLocWithOffset(ColonPos);
+}
+
+/// \brief Finds all the named non-static fields of \p Record.
+static std::set<const FieldDecl *>
+getAllNamedFields(const CXXRecordDecl *Record) {
+ std::set<const FieldDecl *> Result;
+ for (const auto *Field : Record->fields()) {
+ // Static data members are not in this range.
+ if (Field->isUnnamedBitfield())
+ continue;
+ Result.insert(Field);
+ }
+ return Result;
+}
+
+/// \brief Returns the names of the direct bases of \p Record, both virtual and
+/// non-virtual.
+static std::set<const Type *> getAllDirectBases(const CXXRecordDecl *Record) {
+ std::set<const Type *> Result;
+ for (auto Base : Record->bases()) {
+ // CXXBaseSpecifier.
+ const auto *BaseType = Base.getTypeSourceInfo()->getType().getTypePtr();
+ Result.insert(BaseType);
+ }
+ return Result;
+}
+
+/// \brief Returns a matcher that matches member expressions where the base is
+/// the variable declared as \p Var and the accessed member is the one declared
+/// as \p Field.
+internal::Matcher<Expr> accessToFieldInVar(const FieldDecl *Field,
+ const ValueDecl *Var) {
+ return ignoringImpCasts(
+ memberExpr(hasObjectExpression(declRefExpr(to(varDecl(equalsNode(Var))))),
+ member(fieldDecl(equalsNode(Field)))));
+}
+
+/// \brief Check that the given constructor has copy signature and that it
+/// copy-initializes all its bases and members.
+static bool isCopyConstructorAndCanBeDefaulted(ASTContext *Context,
+ const CXXConstructorDecl *Ctor) {
+ // An explicitly-defaulted constructor cannot have default arguments.
+ if (Ctor->getMinRequiredArguments() != 1)
+ return false;
+
+ const auto *Record = Ctor->getParent();
+ const auto *Param = Ctor->getParamDecl(0);
+
+ // Base classes and members that have to be copied.
+ auto BasesToInit = getAllDirectBases(Record);
+ auto FieldsToInit = getAllNamedFields(Record);
+
+ // Ensure that all the bases are copied.
+ for (const auto *Base : BasesToInit) {
+ // The initialization of a base class should be a call to a copy
+ // constructor of the base.
+ if (match(
+ cxxConstructorDecl(forEachConstructorInitializer(cxxCtorInitializer(
+ isBaseInitializer(),
+ withInitializer(cxxConstructExpr(allOf(
+ hasType(equalsNode(Base)),
+ hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
+ argumentCountIs(1),
+ hasArgument(
+ 0, declRefExpr(to(varDecl(equalsNode(Param))))))))))),
+ *Ctor, *Context)
+ .empty())
+ return false;
+ }
+
+ // Ensure that all the members are copied.
+ for (const auto *Field : FieldsToInit) {
+ auto AccessToFieldInParam = accessToFieldInVar(Field, Param);
+ // The initialization is a CXXConstructExpr for class types.
+ if (match(
+ cxxConstructorDecl(forEachConstructorInitializer(cxxCtorInitializer(
+ isMemberInitializer(), forField(equalsNode(Field)),
+ withInitializer(anyOf(
+ AccessToFieldInParam,
+ cxxConstructExpr(allOf(
+ hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
+ argumentCountIs(1),
+ hasArgument(0, AccessToFieldInParam)))))))),
+ *Ctor, *Context)
+ .empty())
+ return false;
+ }
+
+ // Ensure that we don't do anything else, like initializing an indirect base.
+ return Ctor->getNumCtorInitializers() ==
+ BasesToInit.size() + FieldsToInit.size();
+}
+
+/// \brief Checks that the given method is an overloading of the assignment
+/// operator, has copy signature, returns a reference to "*this" and copies
+/// all its members and subobjects.
+static bool isCopyAssignmentAndCanBeDefaulted(ASTContext *Context,
+ const CXXMethodDecl *Operator) {
+ const auto *Record = Operator->getParent();
+ const auto *Param = Operator->getParamDecl(0);
+
+ // Base classes and members that have to be copied.
+ auto BasesToInit = getAllDirectBases(Record);
+ auto FieldsToInit = getAllNamedFields(Record);
+
+ const auto *Compound = cast<CompoundStmt>(Operator->getBody());
+
+ // The assignment operator definition has to end with the following return
+ // statement:
+ // return *this;
+ if (Compound->body_empty() ||
+ match(returnStmt(has(unaryOperator(hasOperatorName("*"),
+ hasUnaryOperand(cxxThisExpr())))),
+ *Compound->body_back(), *Context)
+ .empty())
+ return false;
+
+ // Ensure that all the bases are copied.
+ for (const auto *Base : BasesToInit) {
+ // Assignment operator of a base class:
+ // Base::operator=(Other);
+ //
+ // Clang translates this into:
+ // ((Base*)this)->operator=((Base)Other);
+ //
+ // So we are looking for a member call that fulfills:
+ if (match(
+ compoundStmt(has(cxxMemberCallExpr(allOf(
+ // - The object is an implicit cast of 'this' to a pointer to
+ // a base class.
+ onImplicitObjectArgument(
+ implicitCastExpr(hasImplicitDestinationType(
+ pointsTo(type(equalsNode(Base)))),
+ hasSourceExpression(cxxThisExpr()))),
+ // - The called method is the operator=.
+ callee(cxxMethodDecl(isCopyAssignmentOperator())),
+ // - The argument is (an implicit cast to a Base of) the
+ // argument taken by "Operator".
+ argumentCountIs(1),
+ hasArgument(0, declRefExpr(to(varDecl(equalsNode(Param))))))))),
+ *Compound, *Context)
+ .empty())
+ return false;
+ }
+
+ // Ensure that all the members are copied.
+ for (const auto *Field : FieldsToInit) {
+ // The assignment of data members:
+ // Field = Other.Field;
+ // Is a BinaryOperator in non-class types, and a CXXOperatorCallExpr
+ // otherwise.
+ auto LHS = memberExpr(hasObjectExpression(cxxThisExpr()),
+ member(fieldDecl(equalsNode(Field))));
+ auto RHS = accessToFieldInVar(Field, Param);
+ if (match(
+ compoundStmt(has(stmt(anyOf(
+ binaryOperator(hasOperatorName("="), hasLHS(LHS), hasRHS(RHS)),
+ cxxOperatorCallExpr(hasOverloadedOperatorName("="),
+ argumentCountIs(2), hasArgument(0, LHS),
+ hasArgument(1, RHS)))))),
+ *Compound, *Context)
+ .empty())
+ return false;
+ }
+
+ // Ensure that we don't do anything else.
+ return Compound->size() == BasesToInit.size() + FieldsToInit.size() + 1;
+}
+
+/// \brief Returns false if the body has any non-whitespace character.
+static bool bodyEmpty(const ASTContext *Context, const CompoundStmt *Body) {
+ bool Invalid = false;
+ StringRef Text = Lexer::getSourceText(
+ CharSourceRange::getCharRange(Body->getLBracLoc().getLocWithOffset(1),
+ Body->getRBracLoc()),
+ Context->getSourceManager(), Context->getLangOpts(), &Invalid);
+ return !Invalid && std::strspn(Text.data(), " \t\r\n") == Text.size();
+}
void UseDefaultCheck::registerMatchers(MatchFinder *Finder) {
if (getLangOpts().CPlusPlus) {
+ // Destructor.
+ Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(SpecialFunction),
+ this);
+ Finder->addMatcher(
+ cxxConstructorDecl(
+ isDefinition(),
+ anyOf(
+ // Default constructor.
+ allOf(unless(hasAnyConstructorInitializer(anything())),
+ parameterCountIs(0)),
+ // Copy constructor.
+ allOf(isCopyConstructor(),
+ // Discard constructors that can be used as a copy
+ // constructor because all the other arguments have
+ // default values.
+ parameterCountIs(1))))
+ .bind(SpecialFunction),
+ this);
+ // Copy-assignment operator.
Finder->addMatcher(
- cxxConstructorDecl(isDefinition(),
- unless(hasAnyConstructorInitializer(anything())),
- parameterCountIs(0))
- .bind(CtorDtor),
+ cxxMethodDecl(isDefinition(), isCopyAssignmentOperator(),
+ // isCopyAssignmentOperator() allows the parameter to be
+ // passed by value, and in this case it cannot be
+ // defaulted.
+ hasParameter(0, hasType(lValueReferenceType())))
+ .bind(SpecialFunction),
this);
- Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(CtorDtor), this);
}
}
void UseDefaultCheck::check(const MatchFinder::MatchResult &Result) {
+ std::string SpecialFunctionName;
+ SourceLocation StartLoc, EndLoc;
+
// Both CXXConstructorDecl and CXXDestructorDecl inherit from CXXMethodDecl.
- const auto *CtorDtorDecl = Result.Nodes.getNodeAs<CXXMethodDecl>(CtorDtor);
+ const auto *SpecialFunctionDecl =
+ Result.Nodes.getNodeAs<CXXMethodDecl>(SpecialFunction);
+
+ // Discard explicitly deleted/defaulted special member functions and those
+ // that are not user-provided (automatically generated).
+ if (SpecialFunctionDecl->isDeleted() ||
+ SpecialFunctionDecl->isExplicitlyDefaulted() ||
+ !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
+ return;
- // Discard explicitly deleted/defaulted constructors/destructors, those that
- // are not user-provided (automatically generated constructor/destructor), and
- // those with non-empty bodies.
- if (CtorDtorDecl->isDeleted() || CtorDtorDecl->isExplicitlyDefaulted() ||
- !CtorDtorDecl->isUserProvided() || !CtorDtorDecl->hasTrivialBody())
+ const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
+ if (!Body)
return;
- const auto *Body = dyn_cast<CompoundStmt>(CtorDtorDecl->getBody());
- // This should never happen, since 'hasTrivialBody' checks that this is
- // actually a CompoundStmt.
- assert(Body && "Definition body is not a CompoundStmt");
-
- diag(CtorDtorDecl->getLocStart(),
- "use '= default' to define a trivial " +
- std::string(dyn_cast<CXXConstructorDecl>(CtorDtorDecl)
- ? "default constructor"
- : "destructor"))
+ // Default locations.
+ StartLoc = Body->getLBracLoc();
+ EndLoc = Body->getRBracLoc();
+
+ // If there are comments inside the body, don't do the change.
+ if (!SpecialFunctionDecl->isCopyAssignmentOperator() &&
+ !bodyEmpty(Result.Context, Body))
+ return;
+
+ if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) {
+ if (Ctor->getNumParams() == 0) {
+ SpecialFunctionName = "default constructor";
+ } else {
+ if (!isCopyConstructorAndCanBeDefaulted(Result.Context, Ctor))
+ return;
+ SpecialFunctionName = "copy constructor";
+ }
+ // If there are constructor initializers, they must be removed.
+ if (Ctor->getNumCtorInitializers() != 0) {
+ StartLoc = getColonLoc(Result.Context, Ctor);
+ if (!StartLoc.isValid())
+ return;
+ }
+ } else if (dyn_cast<CXXDestructorDecl>(SpecialFunctionDecl)) {
+ SpecialFunctionName = "destructor";
+ } else {
+ if (!isCopyAssignmentAndCanBeDefaulted(Result.Context, SpecialFunctionDecl))
+ return;
+ SpecialFunctionName = "copy-assignment operator";
+ }
+
+ diag(SpecialFunctionDecl->getLocStart(),
+ "use '= default' to define a trivial " + SpecialFunctionName)
<< FixItHint::CreateReplacement(
- CharSourceRange::getTokenRange(Body->getLBracLoc(),
- Body->getRBracLoc()),
- "= default;");
+ CharSourceRange::getTokenRange(StartLoc, EndLoc), "= default;");
}
} // namespace modernize
Added: clang-tools-extra/trunk/test/clang-tidy/modernize-use-default-copy.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/modernize-use-default-copy.cpp?rev=251788&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/modernize-use-default-copy.cpp (added)
+++ clang-tools-extra/trunk/test/clang-tidy/modernize-use-default-copy.cpp Mon Nov 2 04:34:19 2015
@@ -0,0 +1,469 @@
+// RUN: %check_clang_tidy %s modernize-use-default %t -- -- -std=c++11 -fno-delayed-template-parsing
+
+// Out of line definition.
+struct OL {
+ OL(const OL &);
+ OL &operator=(const OL &);
+ int Field;
+};
+OL::OL(const OL &Other) : Field(Other.Field) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default' to define a trivial copy constructor [modernize-use-default]
+// CHECK-FIXES: OL::OL(const OL &Other) = default;
+OL &OL::operator=(const OL &Other) {
+ Field = Other.Field;
+ return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:1: warning: use '= default' to define a trivial copy-assignment operator [modernize-use-default]
+// CHECK-FIXES: OL &OL::operator=(const OL &Other) = default;
+
+// Inline.
+struct IL {
+ IL(const IL &Other) : Field(Other.Field) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: IL(const IL &Other) = default;
+ IL &operator=(const IL &Other) {
+ Field = Other.Field;
+ return *this;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use '= default'
+ // CHECK-FIXES: IL &operator=(const IL &Other) = default;
+ int Field;
+};
+
+// Wrong type.
+struct WT {
+ WT(const IL &Other) {}
+ WT &operator=(const IL &);
+};
+WT &WT::operator=(const IL &Other) { return *this; }
+
+// Qualifiers.
+struct Qual {
+ Qual(const Qual &Other) : Field(Other.Field), Volatile(Other.Volatile),
+ Mutable(Other.Mutable), Reference(Other.Reference),
+ Const(Other.Const) {}
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use '= default'
+ // CHECK-FIXES: Qual(const Qual &Other) = default;
+
+ int Field;
+ volatile char Volatile;
+ mutable bool Mutable;
+ const OL &Reference; // This makes this class non-assignable.
+ const IL Const; // This also makes this class non-assignable.
+ static int Static;
+};
+
+// Wrong init arguments.
+struct WI {
+ WI(const WI &Other) : Field1(Other.Field1), Field2(Other.Field1) {}
+ WI &operator=(const WI &);
+ int Field1, Field2;
+};
+WI &WI::operator=(const WI &Other) {
+ Field1 = Other.Field1;
+ Field2 = Other.Field1;
+ return *this;
+}
+
+// Missing field.
+struct MF {
+ MF(const MF &Other) : Field1(Other.Field1), Field2(Other.Field2) {}
+ MF &operator=(const MF &);
+ int Field1, Field2, Field3;
+};
+MF &MF::operator=(const MF &Other) {
+ Field1 = Other.Field1;
+ Field2 = Other.Field2;
+ return *this;
+}
+
+struct Comments {
+ Comments(const Comments &Other)
+ /* don't delete */ : /* this comment */ Field(Other.Field) {}
+ int Field;
+};
+
+struct MoreComments {
+ MoreComments(const MoreComments &Other) /* this comment is OK */
+ : Field(Other.Field) {}
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use '= default'
+ // CHECK-FIXES: MoreComments(const MoreComments &Other) /* this comment is OK */
+ // CHECK-FIXES-NEXT: = default;
+ int Field;
+};
+
+struct ColonInComment {
+ ColonInComment(const ColonInComment &Other) /* : */ : Field(Other.Field) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: ColonInComment(const ColonInComment &Other) /* : */ = default;
+ int Field;
+};
+
+// No members or bases (in particular, no colon).
+struct Empty {
+ Empty(const Empty &Other) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: Empty(const Empty &Other) = default;
+ Empty &operator=(const Empty &);
+};
+Empty &Empty::operator=(const Empty &Other) { return *this; }
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default'
+// CHECK-FIXES: Empty &Empty::operator=(const Empty &Other) = default;
+
+// Bit fields.
+struct BF {
+ BF() = default;
+ BF(const BF &Other) : Field1(Other.Field1), Field2(Other.Field2), Field3(Other.Field3),
+ Field4(Other.Field4) {}
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use '= default'
+ // CHECK-FIXES: BF(const BF &Other) = default;
+ BF &operator=(const BF &);
+
+ unsigned Field1 : 3;
+ int : 7;
+ char Field2 : 6;
+ int : 0;
+ int Field3 : 24;
+ unsigned char Field4;
+};
+BF &BF::operator=(const BF &Other) {
+ Field1 = Other.Field1;
+ Field2 = Other.Field2;
+ Field3 = Other.Field3;
+ Field4 = Other.Field4;
+ return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-7]]:1: warning: use '= default'
+// CHECK-FIXES: BF &BF::operator=(const BF &Other) = default;
+
+// Base classes.
+struct BC : IL, OL, BF {
+ BC(const BC &Other) : IL(Other), OL(Other), BF(Other) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: BC(const BC &Other) = default;
+ BC &operator=(const BC &Other);
+};
+BC &BC::operator=(const BC &Other) {
+ IL::operator=(Other);
+ OL::operator=(Other);
+ BF::operator=(Other);
+ return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-6]]:1: warning: use '= default'
+// CHECK-FIXES: BC &BC::operator=(const BC &Other) = default;
+
+// Base classes with member.
+struct BCWM : IL, OL {
+ BCWM(const BCWM &Other) : IL(Other), OL(Other), Bf(Other.Bf) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: BCWM(const BCWM &Other) = default;
+ BCWM &operator=(const BCWM &);
+ BF Bf;
+};
+BCWM &BCWM::operator=(const BCWM &Other) {
+ IL::operator=(Other);
+ OL::operator=(Other);
+ Bf = Other.Bf;
+ return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-6]]:1: warning: use '= default'
+// CHECK-FIXES: BCWM &BCWM::operator=(const BCWM &Other) = default;
+
+// Missing base class.
+struct MBC : IL, OL, BF {
+ MBC(const MBC &Other) : IL(Other), OL(Other) {}
+ MBC &operator=(const MBC &);
+};
+MBC &MBC::operator=(const MBC &Other) {
+ IL::operator=(Other);
+ OL::operator=(Other);
+ return *this;
+}
+
+// Base classes, incorrect parameter.
+struct BCIP : BCWM, BF {
+ BCIP(const BCIP &Other) : BCWM(Other), BF(Other.Bf) {}
+ BCIP &operator=(const BCIP &);
+};
+BCIP &BCIP::operator=(const BCIP &Other) {
+ BCWM::operator=(Other);
+ BF::operator=(Other.Bf);
+ return *this;
+}
+
+// Virtual base classes.
+struct VA : virtual OL {};
+struct VB : virtual OL {};
+struct VBC : VA, VB, virtual OL {
+ // OL is the first thing that is going to be initialized, despite the fact
+ // that it is the last in the list of bases, because it is virtual and there
+ // is a virtual OL at the beginning of VA (which is the same).
+ VBC(const VBC &Other) : OL(Other), VA(Other), VB(Other) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: VBC(const VBC &Other) = default;
+ VBC &operator=(const VBC &Other);
+};
+VBC &VBC::operator=(const VBC &Other) {
+ OL::operator=(Other);
+ VA::operator=(Other);
+ VB::operator=(Other);
+ return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-6]]:1: warning: use '= default'
+// CHECK-FIXES: VBC &VBC::operator=(const VBC &Other) = default;
+
+// Indirect base.
+struct IB : VBC {
+ IB(const IB &Other) : OL(Other), VBC(Other) {}
+ IB &operator=(const IB &);
+};
+IB &IB::operator=(const IB &Other) {
+ OL::operator=(Other);
+ VBC::operator=(Other);
+ return *this;
+}
+
+// Class template.
+template <class T>
+struct Template {
+ Template() = default;
+ Template(const Template &Other) : Field(Other.Field) {}
+ Template &operator=(const Template &Other);
+ void foo(const T &t);
+ int Field;
+};
+template <class T>
+Template<T> &Template<T>::operator=(const Template<T> &Other) {
+ Field = Other.Field;
+ return *this;
+}
+Template<int> T1;
+
+// Dependent types.
+template <class T>
+struct DT1 {
+ DT1() = default;
+ DT1(const DT1 &Other) : Field(Other.Field) {}
+ DT1 &operator=(const DT1 &);
+ T Field;
+};
+template <class T>
+DT1<T> &DT1<T>::operator=(const DT1<T> &Other) {
+ Field = Other.Field;
+ return *this;
+}
+DT1<int> Dt1;
+
+template <class T>
+struct DT2 {
+ DT2() = default;
+ DT2(const DT2 &Other) : Field(Other.Field), Dependent(Other.Dependent) {}
+ DT2 &operator=(const DT2 &);
+ T Field;
+ typename T::TT Dependent;
+};
+template <class T>
+DT2<T> &DT2<T>::operator=(const DT2<T> &Other) {
+ Field = Other.Field;
+ Dependent = Other.Dependent;
+ return *this;
+}
+struct T {
+ typedef int TT;
+};
+DT2<T> Dt2;
+
+// Default arguments.
+struct DA {
+ DA(int Int);
+ DA(const DA &Other = DA(0)) : Field1(Other.Field1), Field2(Other.Field2) {}
+ DA &operator=(const DA &);
+ int Field1;
+ char Field2;
+};
+// Overloaded operator= cannot have a default argument.
+DA &DA::operator=(const DA &Other) {
+ Field1 = Other.Field1;
+ Field2 = Other.Field2;
+ return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:1: warning: use '= default'
+// CHECK-FIXES: DA &DA::operator=(const DA &Other) = default;
+
+struct DA2 {
+ // Can be used as copy-constructor but cannot be explicitly defaulted.
+ DA2(const DA &Other, int Def = 0) {}
+};
+
+// Default initialization.
+struct DI {
+ DI(const DI &Other) : Field1(Other.Field1), Field2(Other.Field2) {}
+ int Field1;
+ int Field2 = 0;
+ int Fiedl3;
+};
+
+// Statement inside body.
+void foo();
+struct SIB {
+ SIB(const SIB &Other) : Field(Other.Field) { foo(); }
+ SIB &operator=(const SIB &);
+ int Field;
+};
+SIB &SIB::operator=(const SIB &Other) {
+ Field = Other.Field;
+ foo();
+ return *this;
+}
+
+// Comment inside body.
+struct CIB {
+ CIB(const CIB &Other) : Field(Other.Field) { /* Don't erase this */
+ }
+ CIB &operator=(const CIB &);
+ int Field;
+};
+CIB &CIB::operator=(const CIB &Other) {
+ Field = Other.Field;
+ // FIXME: don't erase this comment.
+ return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:1: warning: use '= default'
+// CHECK-FIXES: CIB &CIB::operator=(const CIB &Other) = default;
+
+// Take non-const reference as argument.
+struct NCRef {
+ NCRef(NCRef &Other) : Field1(Other.Field1), Field2(Other.Field2) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: NCRef(NCRef &Other) = default;
+ NCRef &operator=(NCRef &);
+ int Field1, Field2;
+};
+NCRef &NCRef::operator=(NCRef &Other) {
+ Field1 = Other.Field1;
+ Field2 = Other.Field2;
+ return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:1: warning: use '= default'
+// CHECK-FIXES: NCRef &NCRef::operator=(NCRef &Other) = default;
+
+// Already defaulted.
+struct IAD {
+ IAD(const IAD &Other) = default;
+ IAD &operator=(const IAD &Other) = default;
+};
+
+struct OAD {
+ OAD(const OAD &Other);
+ OAD &operator=(const OAD &);
+};
+OAD::OAD(const OAD &Other) = default;
+OAD &OAD::operator=(const OAD &Other) = default;
+
+// Deleted.
+struct ID {
+ ID(const ID &Other) = delete;
+ ID &operator=(const ID &Other) = delete;
+};
+
+// Non-reference parameter.
+struct NRef {
+ NRef &operator=(NRef Other);
+ int Field1;
+};
+NRef &NRef::operator=(NRef Other) {
+ Field1 = Other.Field1;
+ return *this;
+}
+
+// RValue reference parameter.
+struct RVR {
+ RVR(RVR &&Other) {}
+ RVR &operator=(RVR &&);
+};
+RVR &RVR::operator=(RVR &&Other) { return *this; }
+
+// Similar function.
+struct SF {
+ SF &foo(const SF &);
+ int Field1;
+};
+SF &SF::foo(const SF &Other) {
+ Field1 = Other.Field1;
+ return *this;
+}
+
+// No return.
+struct NR {
+ NR &operator=(const NR &);
+};
+NR &NR::operator=(const NR &Other) {}
+
+// Return misplaced.
+struct RM {
+ RM &operator=(const RM &);
+ int Field;
+};
+RM &RM::operator=(const RM &Other) {
+ return *this;
+ Field = Other.Field;
+}
+
+// Wrong return value.
+struct WRV {
+ WRV &operator=(WRV &);
+};
+WRV &WRV::operator=(WRV &Other) {
+ return Other;
+}
+
+// Wrong return type.
+struct WRT : IL {
+ IL &operator=(const WRT &);
+};
+IL &WRT::operator=(const WRT &Other) {
+ return *this;
+}
+
+// Try-catch.
+struct ITC {
+ ITC(const ITC &Other)
+ try : Field(Other.Field) {
+ } catch (...) {
+ }
+ ITC &operator=(const ITC &Other) try {
+ Field = Other.Field;
+ } catch (...) {
+ }
+ int Field;
+};
+
+struct OTC {
+ OTC(const OTC &);
+ OTC &operator=(const OTC &);
+ int Field;
+};
+OTC::OTC(const OTC &Other) try : Field(Other.Field) {
+} catch (...) {
+}
+OTC &OTC::operator=(const OTC &Other) try {
+ Field = Other.Field;
+} catch (...) {
+}
+
+// FIXME: the check is not able to detect exception specification.
+// noexcept(true).
+struct NET {
+ // This is the default.
+ //NET(const NET &Other) noexcept {}
+ NET &operator=(const NET &Other) noexcept;
+};
+//NET &NET::operator=(const NET &Other) noexcept { return *this; }
+
+// noexcept(false).
+struct NEF {
+ // This is the default.
+ //NEF(const NEF &Other) noexcept(false) {}
+ NEF &operator=(const NEF &Other) noexcept(false);
+};
+//NEF &NEF::operator=(const NEF &Other) noexcept(false) { return *this; }
Modified: clang-tools-extra/trunk/test/clang-tidy/modernize-use-default.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/modernize-use-default.cpp?rev=251788&r1=251787&r2=251788&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/modernize-use-default.cpp (original)
+++ clang-tools-extra/trunk/test/clang-tidy/modernize-use-default.cpp Mon Nov 2 04:34:19 2015
@@ -1,137 +1,159 @@
// RUN: %check_clang_tidy %s modernize-use-default %t -- -- -std=c++11 -fno-delayed-template-parsing
-class A {
+// Out of line definition.
+class OL {
public:
- A();
- ~A();
+ OL();
+ ~OL();
};
-A::A() {}
+OL::OL() {}
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default' to define a trivial default constructor [modernize-use-default]
-// CHECK-FIXES: A::A() = default;
-A::~A() {}
+// CHECK-FIXES: OL::OL() = default;
+OL::~OL() {}
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default' to define a trivial destructor [modernize-use-default]
-// CHECK-FIXES: A::~A() = default;
+// CHECK-FIXES: OL::~OL() = default;
// Inline definitions.
-class B {
+class IL {
public:
- B() {}
+ IL() {}
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
- // CHECK-FIXES: B() = default;
- ~B() {}
+ // CHECK-FIXES: IL() = default;
+ ~IL() {}
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
- // CHECK-FIXES: ~B() = default;
+ // CHECK-FIXES: ~IL() = default;
};
+// Non-empty body.
void f();
-
-class C {
+class NE {
public:
- // Non-empty constructor body.
- C() { f(); }
- // Non-empty destructor body.
- ~C() { f(); }
+ NE() { f(); }
+ ~NE() { f(); }
};
-class D {
+// Initializer or arguments.
+class IA {
public:
// Constructor with initializer.
- D() : Field(5) {}
+ IA() : Field(5) {}
// Constructor with arguments.
- D(int Arg1, int Arg2) {}
+ IA(int Arg1, int Arg2) {}
int Field;
};
// Private constructor/destructor.
-class E {
- E() {}
+class Priv {
+ Priv() {}
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
- // CHECK-FIXES: E() = default;
- ~E() {}
+ // CHECK-FIXES: Priv() = default;
+ ~Priv() {}
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
- // CHECK-FIXES: ~E() = default;
+ // CHECK-FIXES: ~Priv() = default;
};
// struct.
-struct F {
- F() {}
+struct ST {
+ ST() {}
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
- // CHECK-FIXES: F() = default;
- ~F() {}
+ // CHECK-FIXES: ST() = default;
+ ~ST() {}
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
- // CHECK-FIXES: F() = default;
+ // CHECK-FIXES: ST() = default;
};
// Deleted constructor/destructor.
-class G {
+class Del {
public:
- G() = delete;
- ~G() = delete;
+ Del() = delete;
+ ~Del() = delete;
};
// Do not remove other keywords.
-class H {
+class KW {
public:
- explicit H() {}
+ explicit KW() {}
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
- // CHECK-FIXES: explicit H() = default;
- virtual ~H() {}
+ // CHECK-FIXES: explicit KW() = default;
+ virtual ~KW() {}
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
- // CHECK-FIXES: virtual ~H() = default;
+ // CHECK-FIXES: virtual ~KW() = default;
};
// Nested class.
-struct I {
- struct II {
- II() {}
+struct N {
+ struct NN {
+ NN() {}
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default'
- // CHECK-FIXES: II() = default;
- ~II() {}
+ // CHECK-FIXES: NN() = default;
+ ~NN() {}
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default'
- // CHECK-FIXES: ~II() = default;
+ // CHECK-FIXES: ~NN() = default;
};
int Int;
};
// Class template.
template <class T>
-class J {
+class Temp {
public:
- J() {}
+ Temp() {}
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
- // CHECK-FIXES: J() = default;
- ~J() {}
+ // CHECK-FIXES: Temp() = default;
+ ~Temp() {}
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
- // CHECK-FIXES: ~J() = default;
+ // CHECK-FIXES: ~Temp() = default;
};
// Non user-provided constructor/destructor.
-struct K {
+struct Imp {
int Int;
};
void g() {
- K *PtrK = new K();
- PtrK->~K();
- delete PtrK;
+ Imp *PtrImp = new Imp();
+ PtrImp->~Imp();
+ delete PtrImp;
}
// Already using default.
-struct L {
- L() = default;
- ~L() = default;
-};
-struct M {
- M();
- ~M();
+struct IDef {
+ IDef() = default;
+ ~IDef() = default;
+};
+struct ODef {
+ ODef();
+ ~ODef();
};
-M::M() = default;
-M::~M() = default;
+ODef::ODef() = default;
+ODef::~ODef() = default;
// Delegating constructor and overriden destructor.
-struct N : H {
- N() : H() {}
- ~N() override {}
+struct DC : KW {
+ DC() : KW() {}
+ ~DC() override {}
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
- // CHECK-FIXES: ~N() override = default;
+ // CHECK-FIXES: ~DC() override = default;
+};
+
+struct Comments {
+ Comments() {
+ // Don't erase comments inside the body.
+ }
+ ~Comments() {
+ // Don't erase comments inside the body.
+ }
+};
+
+// Try-catch.
+struct ITC {
+ ITC() try {} catch(...) {}
+ ~ITC() try {} catch(...) {}
+};
+
+struct OTC {
+ OTC();
+ ~OTC();
};
+OTC::OTC() try {} catch(...) {}
+OTC::~OTC() try {} catch(...) {}
More information about the cfe-commits
mailing list