Index: clang-tools-extra/trunk/clang-tidy/add_new_check.py =================================================================== --- clang-tools-extra/trunk/clang-tidy/add_new_check.py +++ clang-tools-extra/trunk/clang-tidy/add_new_check.py @@ -226,8 +226,13 @@ def format_link(doc_file): check_name = doc_file.replace('.rst', '') with open(os.path.join(docs_dir, doc_file), 'r') as doc: + content = doc.read() + match = re.search('.*:orphan:.*', content) + if match: + return '' + match = re.search('.*:http-equiv=refresh: \d+;URL=(.*).html.*', - doc.read()) + content) if match: return ' %(check)s (redirects to %(target)s) <%(check)s>\n' % { 'check': check_name, Index: clang-tools-extra/trunk/clang-tidy/modernize/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/clang-tidy/modernize/CMakeLists.txt +++ clang-tools-extra/trunk/clang-tidy/modernize/CMakeLists.txt @@ -16,8 +16,8 @@ ShrinkToFitCheck.cpp UseAutoCheck.cpp UseBoolLiteralsCheck.cpp - UseDefaultCheck.cpp UseEmplaceCheck.cpp + UseEqualsDefaultCheck.cpp UseEqualsDeleteCheck.cpp UseNullptrCheck.cpp UseOverrideCheck.cpp Index: clang-tools-extra/trunk/clang-tidy/modernize/ModernizeTidyModule.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/modernize/ModernizeTidyModule.cpp +++ clang-tools-extra/trunk/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -22,8 +22,8 @@ #include "ShrinkToFitCheck.h" #include "UseAutoCheck.h" #include "UseBoolLiteralsCheck.h" -#include "UseDefaultCheck.h" #include "UseEmplaceCheck.h" +#include "UseEqualsDefaultCheck.h" #include "UseEqualsDeleteCheck.h" #include "UseNullptrCheck.h" #include "UseOverrideCheck.h" @@ -56,8 +56,8 @@ CheckFactories.registerCheck("modernize-use-auto"); CheckFactories.registerCheck( "modernize-use-bool-literals"); - CheckFactories.registerCheck("modernize-use-default"); CheckFactories.registerCheck("modernize-use-emplace"); + CheckFactories.registerCheck("modernize-use-equals-default"); CheckFactories.registerCheck( "modernize-use-equals-delete"); CheckFactories.registerCheck("modernize-use-nullptr"); Index: clang-tools-extra/trunk/clang-tidy/modernize/UseDefaultCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/modernize/UseDefaultCheck.h +++ clang-tools-extra/trunk/clang-tidy/modernize/UseDefaultCheck.h @@ -1,50 +0,0 @@ -//===--- UseDefaultCheck.h - clang-tidy--------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_H - -#include "../ClangTidy.h" - -namespace clang { -namespace tidy { -namespace modernize { - -/// \brief Replace default bodies of special member functions with '= default;'. -/// \code -/// struct A { -/// A() {} -/// ~A(); -/// }; -/// A::~A() {} -/// \endcode -/// Is converted to: -/// \code -/// struct A { -/// A() = default; -/// ~A(); -/// }; -/// A::~A() = default; -/// \endcode -/// -/// For the user-facing documentation see: -/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-default.html -class UseDefaultCheck : public ClangTidyCheck { -public: - UseDefaultCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} - void registerMatchers(ast_matchers::MatchFinder *Finder) override; - void check(const ast_matchers::MatchFinder::MatchResult &Result) override; -}; - -} // namespace modernize -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_H Index: clang-tools-extra/trunk/clang-tidy/modernize/UseDefaultCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/modernize/UseDefaultCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/modernize/UseDefaultCheck.cpp @@ -1,299 +0,0 @@ -//===--- UseDefaultCheck.cpp - clang-tidy----------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "UseDefaultCheck.h" -#include "clang/AST/ASTContext.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/Lex/Lexer.h" - -using namespace clang::ast_matchers; - -namespace clang { -namespace tidy { -namespace modernize { - -static const char SpecialFunction[] = "SpecialFunction"; - -/// \brief Finds all the named non-static fields of \p Record. -static std::set -getAllNamedFields(const CXXRecordDecl *Record) { - std::set 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 getAllDirectBases(const CXXRecordDecl *Record) { - std::set 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 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(Operator->getBody()); - - // The assignment operator definition has to end with the following return - // statement: - // return *this; - if (Compound->body_empty() || - match(returnStmt(has(ignoringParenImpCasts(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(ignoringParenImpCasts(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(ignoringParenImpCasts(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(isWritten())), - 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( - 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); - } -} - -void UseDefaultCheck::check(const MatchFinder::MatchResult &Result) { - std::string SpecialFunctionName; - - // Both CXXConstructorDecl and CXXDestructorDecl inherit from CXXMethodDecl. - const auto *SpecialFunctionDecl = - Result.Nodes.getNodeAs(SpecialFunction); - - // Discard explicitly deleted/defaulted special member functions and those - // that are not user-provided (automatically generated). - if (SpecialFunctionDecl->isDeleted() || - SpecialFunctionDecl->isExplicitlyDefaulted() || - SpecialFunctionDecl->isLateTemplateParsed() || - SpecialFunctionDecl->isTemplateInstantiation() || - !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody()) - return; - - const auto *Body = dyn_cast(SpecialFunctionDecl->getBody()); - if (!Body) - return; - - // If there is code inside the body, don't warn. - if (!SpecialFunctionDecl->isCopyAssignmentOperator() && !Body->body_empty()) - return; - - // If there are comments inside the body, don't do the change. - bool ApplyFix = SpecialFunctionDecl->isCopyAssignmentOperator() || - bodyEmpty(Result.Context, Body); - - std::vector RemoveInitializers; - - if (const auto *Ctor = dyn_cast(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. - for (const auto *Init : Ctor->inits()) { - RemoveInitializers.emplace_back( - FixItHint::CreateRemoval(Init->getSourceRange())); - } - } - } else if (isa(SpecialFunctionDecl)) { - SpecialFunctionName = "destructor"; - } else { - if (!isCopyAssignmentAndCanBeDefaulted(Result.Context, SpecialFunctionDecl)) - return; - SpecialFunctionName = "copy-assignment operator"; - } - - // The location of the body is more useful inside a macro as spelling and - // expansion locations are reported. - SourceLocation Location = SpecialFunctionDecl->getLocation(); - if (Location.isMacroID()) - Location = Body->getLocStart(); - - auto Diag = diag(Location, "use '= default' to define a trivial " + - SpecialFunctionName); - - if (ApplyFix) - Diag << FixItHint::CreateReplacement(Body->getSourceRange(), "= default;") - << RemoveInitializers; -} - -} // namespace modernize -} // namespace tidy -} // namespace clang Index: clang-tools-extra/trunk/clang-tidy/modernize/UseEqualsDefaultCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/modernize/UseEqualsDefaultCheck.h +++ clang-tools-extra/trunk/clang-tidy/modernize/UseEqualsDefaultCheck.h @@ -0,0 +1,50 @@ +//===--- UseEqualsDefaultCheck.h - clang-tidy--------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EQUALS_DEFAULT_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EQUALS_DEFAULT_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace modernize { + +/// \brief Replace default bodies of special member functions with '= default;'. +/// \code +/// struct A { +/// A() {} +/// ~A(); +/// }; +/// A::~A() {} +/// \endcode +/// Is converted to: +/// \code +/// struct A { +/// A() = default; +/// ~A(); +/// }; +/// A::~A() = default; +/// \endcode +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-equals-default.html +class UseEqualsDefaultCheck : public ClangTidyCheck { +public: + UseEqualsDefaultCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EQUALS_DEFAULT_H Index: clang-tools-extra/trunk/clang-tidy/modernize/UseEqualsDefaultCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/modernize/UseEqualsDefaultCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/modernize/UseEqualsDefaultCheck.cpp @@ -0,0 +1,299 @@ +//===--- UseEqualsDefaultCheck.cpp - clang-tidy----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "UseEqualsDefaultCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { + +static const char SpecialFunction[] = "SpecialFunction"; + +/// \brief Finds all the named non-static fields of \p Record. +static std::set +getAllNamedFields(const CXXRecordDecl *Record) { + std::set 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 getAllDirectBases(const CXXRecordDecl *Record) { + std::set 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 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(Operator->getBody()); + + // The assignment operator definition has to end with the following return + // statement: + // return *this; + if (Compound->body_empty() || + match(returnStmt(has(ignoringParenImpCasts(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(ignoringParenImpCasts(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(ignoringParenImpCasts(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 UseEqualsDefaultCheck::registerMatchers(MatchFinder *Finder) { + if (getLangOpts().CPlusPlus) { + // Destructor. + Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(SpecialFunction), + this); + Finder->addMatcher( + cxxConstructorDecl( + isDefinition(), + anyOf( + // Default constructor. + allOf(unless(hasAnyConstructorInitializer(isWritten())), + 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( + 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); + } +} + +void UseEqualsDefaultCheck::check(const MatchFinder::MatchResult &Result) { + std::string SpecialFunctionName; + + // Both CXXConstructorDecl and CXXDestructorDecl inherit from CXXMethodDecl. + const auto *SpecialFunctionDecl = + Result.Nodes.getNodeAs(SpecialFunction); + + // Discard explicitly deleted/defaulted special member functions and those + // that are not user-provided (automatically generated). + if (SpecialFunctionDecl->isDeleted() || + SpecialFunctionDecl->isExplicitlyDefaulted() || + SpecialFunctionDecl->isLateTemplateParsed() || + SpecialFunctionDecl->isTemplateInstantiation() || + !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody()) + return; + + const auto *Body = dyn_cast(SpecialFunctionDecl->getBody()); + if (!Body) + return; + + // If there is code inside the body, don't warn. + if (!SpecialFunctionDecl->isCopyAssignmentOperator() && !Body->body_empty()) + return; + + // If there are comments inside the body, don't do the change. + bool ApplyFix = SpecialFunctionDecl->isCopyAssignmentOperator() || + bodyEmpty(Result.Context, Body); + + std::vector RemoveInitializers; + + if (const auto *Ctor = dyn_cast(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. + for (const auto *Init : Ctor->inits()) { + RemoveInitializers.emplace_back( + FixItHint::CreateRemoval(Init->getSourceRange())); + } + } + } else if (isa(SpecialFunctionDecl)) { + SpecialFunctionName = "destructor"; + } else { + if (!isCopyAssignmentAndCanBeDefaulted(Result.Context, SpecialFunctionDecl)) + return; + SpecialFunctionName = "copy-assignment operator"; + } + + // The location of the body is more useful inside a macro as spelling and + // expansion locations are reported. + SourceLocation Location = SpecialFunctionDecl->getLocation(); + if (Location.isMacroID()) + Location = Body->getLocStart(); + + auto Diag = diag(Location, "use '= default' to define a trivial " + + SpecialFunctionName); + + if (ApplyFix) + Diag << FixItHint::CreateReplacement(Body->getSourceRange(), "= default;") + << RemoveInitializers; +} + +} // namespace modernize +} // namespace tidy +} // namespace clang Index: clang-tools-extra/trunk/docs/ReleaseNotes.rst =================================================================== --- clang-tools-extra/trunk/docs/ReleaseNotes.rst +++ clang-tools-extra/trunk/docs/ReleaseNotes.rst @@ -91,6 +91,9 @@ `_ check now warns about variable declarations that are initialized with a cast. +- The modernize-use-default check has been renamed to `modernize-use-equals-default + `_. + - New `modernize-use-equals-delete `_ check Index: clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst @@ -18,7 +18,7 @@ cert-err61-cpp (redirects to misc-throw-by-value-catch-by-reference) cert-fio38-c (redirects to misc-non-copyable-objects) cert-flp30-c - cert-msc30-c (redirects to cert-limited-randomness) + cert-msc30-c (redirects to cert-msc50-cpp) cert-msc50-cpp cert-oop11-cpp (redirects to misc-move-constructor-init) cppcoreguidelines-interfaces-global-init @@ -109,8 +109,8 @@ modernize-shrink-to-fit modernize-use-auto modernize-use-bool-literals - modernize-use-default modernize-use-emplace + modernize-use-equals-default modernize-use-equals-delete modernize-use-nullptr modernize-use-override Index: clang-tools-extra/trunk/docs/clang-tidy/checks/modernize-use-default.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/modernize-use-default.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/modernize-use-default.rst @@ -1,28 +1,10 @@ +:orphan: .. title:: clang-tidy - modernize-use-default +.. meta:: + :http-equiv=refresh: 5;URL=modernize-use-equals-default.html modernize-use-default ===================== -This check replaces default bodies of special member functions with ``= -default;``. The explicitly defaulted function declarations enable more -opportunities in optimization, because the compiler might treat explicitly -defaulted functions as trivial. - -.. code-block:: c++ - - struct A { - A() {} - ~A(); - }; - A::~A() {} - - // becomes - - struct A { - A() = default; - ~A(); - }; - A::~A() = default; - -.. note:: - Move-constructor and move-assignment operator are not supported yet. +This check has been renamed to +`modernize-use-equals-default `_. Index: clang-tools-extra/trunk/docs/clang-tidy/checks/modernize-use-equals-default.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/modernize-use-equals-default.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/modernize-use-equals-default.rst @@ -0,0 +1,28 @@ +.. title:: clang-tidy - modernize-use-equals-default + +modernize-use-equals-default +============================ + +This check replaces default bodies of special member functions with ``= +default;``. The explicitly defaulted function declarations enable more +opportunities in optimization, because the compiler might treat explicitly +defaulted functions as trivial. + +.. code-block:: c++ + + struct A { + A() {} + ~A(); + }; + A::~A() {} + + // becomes + + struct A { + A() = default; + ~A(); + }; + A::~A() = default; + +.. note:: + Move-constructor and move-assignment operator are not supported yet. Index: clang-tools-extra/trunk/test/clang-tidy/modernize-use-default-copy.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/modernize-use-default-copy.cpp +++ clang-tools-extra/trunk/test/clang-tidy/modernize-use-default-copy.cpp @@ -1,497 +0,0 @@ -// RUN: %check_clang_tidy %s modernize-use-default %t -- -- -std=c++11 -fno-delayed-template-parsing -fexceptions - -// 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]]:5: 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]]:9: 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]]:7: 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) - // CHECK-FIXES: = 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) {} - // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use '= default' - // CHECK-FIXES: /* don't delete */ = default; - 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]]:15: 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) {{$}} - // CHECK-FIXES: = 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]]:9: 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]]:9: 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]]:13: 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]]:11: 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 -struct Template { - Template() = default; - Template(const Template &Other) : Field(Other.Field) {} - Template &operator=(const Template &Other); - void foo(const T &t); - int Field; -}; -template -Template &Template::operator=(const Template &Other) { - Field = Other.Field; - return *this; -} -Template T1; - -// Dependent types. -template -struct DT1 { - DT1() = default; - DT1(const DT1 &Other) : Field(Other.Field) {} - DT1 &operator=(const DT1 &); - T Field; -}; -template -DT1 &DT1::operator=(const DT1 &Other) { - Field = Other.Field; - return *this; -} -DT1 Dt1; - -template -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 -DT2 &DT2::operator=(const DT2 &Other) { - Field = Other.Field; - Dependent = Other.Dependent; - return *this; -} -struct T { - typedef int TT; -}; -DT2 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]]:9: 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 */ - } - // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use '= default' - 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]]:11: 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]]:15: 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; } - -#define STRUCT_WITH_COPY_CONSTRUCT(_base, _type) \ - struct _type { \ - _type(const _type &v) : value(v.value) {} \ - _base value; \ - }; - -STRUCT_WITH_COPY_CONSTRUCT(unsigned char, Hex8CopyConstruct) -// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default' to define a trivial copy constructor -// CHECK-MESSAGES: :[[@LINE-6]]:44: note: - -#define STRUCT_WITH_COPY_ASSIGN(_base, _type) \ - struct _type { \ - _type &operator=(const _type &rhs) { \ - value = rhs.value; \ - return *this; \ - } \ - _base value; \ - }; - -STRUCT_WITH_COPY_ASSIGN(unsigned char, Hex8CopyAssign) -// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default' to define a trivial copy-assignment operator -// CHECK-MESSAGES: :[[@LINE-9]]:40: note: Index: clang-tools-extra/trunk/test/clang-tidy/modernize-use-default-delayed.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/modernize-use-default-delayed.cpp +++ clang-tools-extra/trunk/test/clang-tidy/modernize-use-default-delayed.cpp @@ -1,8 +0,0 @@ -// RUN: clang-tidy %s -checks=-*,modernize-use-default -- -std=c++11 -fdelayed-template-parsing -fexceptions | count 0 -// Note: this test expects no diagnostics, but FileCheck cannot handle that, -// hence the use of | count 0. - -template -struct S { - S& operator=(const S&) { return *this; } -}; Index: clang-tools-extra/trunk/test/clang-tidy/modernize-use-default.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/modernize-use-default.cpp +++ clang-tools-extra/trunk/test/clang-tidy/modernize-use-default.cpp @@ -1,209 +0,0 @@ -// RUN: %check_clang_tidy %s modernize-use-default %t -- -- -std=c++11 -fno-delayed-template-parsing -fexceptions - -// Out of line definition. -class OL { -public: - OL(); - ~OL(); -}; - -OL::OL() {} -// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default' to define a trivial default constructor [modernize-use-default] -// CHECK-FIXES: OL::OL() = default; -OL::~OL() {} -// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default' to define a trivial destructor [modernize-use-default] -// CHECK-FIXES: OL::~OL() = default; - -// Inline definitions. -class IL { -public: - IL() {} - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' - // CHECK-FIXES: IL() = default; - ~IL() {} - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' - // CHECK-FIXES: ~IL() = default; -}; - -// Non-empty body. -void f(); -class NE { -public: - NE() { f(); } - ~NE() { f(); } -}; - -// Initializer or arguments. -class IA { -public: - // Constructor with initializer. - IA() : Field(5) {} - // Constructor with arguments. - IA(int Arg1, int Arg2) {} - int Field; -}; - -// Default member initializer -class DMI { -public: - DMI() {} - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' - // CHECK-FIXES: DMI() = default; - int Field = 5; -}; - -// Class member -class CM { -public: - CM() {} - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' - // CHECK-FIXES: CM() = default; - OL o; -}; - -// Private constructor/destructor. -class Priv { - Priv() {} - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' - // CHECK-FIXES: Priv() = default; - ~Priv() {} - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' - // CHECK-FIXES: ~Priv() = default; -}; - -// struct. -struct ST { - ST() {} - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' - // CHECK-FIXES: ST() = default; - ~ST() {} - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' - // CHECK-FIXES: ST() = default; -}; - -// Deleted constructor/destructor. -class Del { -public: - Del() = delete; - ~Del() = delete; -}; - -// Do not remove other keywords. -class KW { -public: - explicit KW() {} - // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use '= default' - // CHECK-FIXES: explicit KW() = default; - virtual ~KW() {} - // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use '= default' - // CHECK-FIXES: virtual ~KW() = default; -}; - -// Nested class. -struct N { - struct NN { - NN() {} - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default' - // CHECK-FIXES: NN() = default; - ~NN() {} - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default' - // CHECK-FIXES: ~NN() = default; - }; - int Int; -}; - -// Class template. -template -class Temp { -public: - Temp() {} - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' - // CHECK-FIXES: Temp() = default; - ~Temp() {} - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' - // CHECK-FIXES: ~Temp() = default; -}; - -// Class template out of line with explicit instantiation. -template -class TempODef { -public: - TempODef(); - ~TempODef(); -}; - -template -TempODef::TempODef() {} -// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use '= default' -// CHECK-FIXES: TempODef::TempODef() = default; -template -TempODef::~TempODef() {} -// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use '= default' -// CHECK-FIXES: TempODef::~TempODef() = default; - -template class TempODef; -template class TempODef; - -// Non user-provided constructor/destructor. -struct Imp { - int Int; -}; -void g() { - Imp *PtrImp = new Imp(); - PtrImp->~Imp(); - delete PtrImp; -} - -// Already using default. -struct IDef { - IDef() = default; - ~IDef() = default; -}; -struct ODef { - ODef(); - ~ODef(); -}; -ODef::ODef() = default; -ODef::~ODef() = default; - -// Delegating constructor and overriden destructor. -struct DC : KW { - DC() : KW() {} - ~DC() override {} - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' - // CHECK-FIXES: ~DC() override = default; -}; - -struct Comments { - Comments() { - // Don't erase comments inside the body. - } - // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use '= default' - ~Comments() { - // Don't erase comments inside the body. - } - // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use '= default' -}; - -// Try-catch. -struct ITC { - ITC() try {} catch(...) {} - ~ITC() try {} catch(...) {} -}; - -struct OTC { - OTC(); - ~OTC(); -}; -OTC::OTC() try {} catch(...) {} -OTC::~OTC() try {} catch(...) {} - -#define STRUCT_WITH_DEFAULT(_base, _type) \ - struct _type { \ - _type() {} \ - _base value; \ - }; - -STRUCT_WITH_DEFAULT(unsigned char, Hex8Default) -// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default' to define a trivial default constructor -// CHECK-MESSAGES: :[[@LINE-6]]:13: note: Index: clang-tools-extra/trunk/test/clang-tidy/modernize-use-equals-default-copy.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/modernize-use-equals-default-copy.cpp +++ clang-tools-extra/trunk/test/clang-tidy/modernize-use-equals-default-copy.cpp @@ -0,0 +1,497 @@ +// RUN: %check_clang_tidy %s modernize-use-equals-default %t -- -- -std=c++11 -fno-delayed-template-parsing -fexceptions + +// 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]]:5: warning: use '= default' to define a trivial copy constructor [modernize-use-equals-default] +// CHECK-FIXES: OL::OL(const OL &Other) = default; +OL &OL::operator=(const OL &Other) { + Field = Other.Field; + return *this; +} +// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: use '= default' to define a trivial copy-assignment operator [modernize-use-equals-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]]:7: 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) + // CHECK-FIXES: = 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) {} + // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use '= default' + // CHECK-FIXES: /* don't delete */ = default; + 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]]:15: 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) {{$}} + // CHECK-FIXES: = 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]]:9: 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]]:9: 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]]:13: 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]]:11: 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 +struct Template { + Template() = default; + Template(const Template &Other) : Field(Other.Field) {} + Template &operator=(const Template &Other); + void foo(const T &t); + int Field; +}; +template +Template &Template::operator=(const Template &Other) { + Field = Other.Field; + return *this; +} +Template T1; + +// Dependent types. +template +struct DT1 { + DT1() = default; + DT1(const DT1 &Other) : Field(Other.Field) {} + DT1 &operator=(const DT1 &); + T Field; +}; +template +DT1 &DT1::operator=(const DT1 &Other) { + Field = Other.Field; + return *this; +} +DT1 Dt1; + +template +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 +DT2 &DT2::operator=(const DT2 &Other) { + Field = Other.Field; + Dependent = Other.Dependent; + return *this; +} +struct T { + typedef int TT; +}; +DT2 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]]:9: 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 */ + } + // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use '= default' + 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]]:11: 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]]:15: 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; } + +#define STRUCT_WITH_COPY_CONSTRUCT(_base, _type) \ + struct _type { \ + _type(const _type &v) : value(v.value) {} \ + _base value; \ + }; + +STRUCT_WITH_COPY_CONSTRUCT(unsigned char, Hex8CopyConstruct) +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default' to define a trivial copy constructor +// CHECK-MESSAGES: :[[@LINE-6]]:44: note: + +#define STRUCT_WITH_COPY_ASSIGN(_base, _type) \ + struct _type { \ + _type &operator=(const _type &rhs) { \ + value = rhs.value; \ + return *this; \ + } \ + _base value; \ + }; + +STRUCT_WITH_COPY_ASSIGN(unsigned char, Hex8CopyAssign) +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default' to define a trivial copy-assignment operator +// CHECK-MESSAGES: :[[@LINE-9]]:40: note: Index: clang-tools-extra/trunk/test/clang-tidy/modernize-use-equals-default-delayed.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/modernize-use-equals-default-delayed.cpp +++ clang-tools-extra/trunk/test/clang-tidy/modernize-use-equals-default-delayed.cpp @@ -0,0 +1,8 @@ +// RUN: clang-tidy %s -checks=-*,modernize-use-equals-default -- -std=c++11 -fdelayed-template-parsing -fexceptions | count 0 +// Note: this test expects no diagnostics, but FileCheck cannot handle that, +// hence the use of | count 0. + +template +struct S { + S& operator=(const S&) { return *this; } +}; Index: clang-tools-extra/trunk/test/clang-tidy/modernize-use-equals-default.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/modernize-use-equals-default.cpp +++ clang-tools-extra/trunk/test/clang-tidy/modernize-use-equals-default.cpp @@ -0,0 +1,209 @@ +// RUN: %check_clang_tidy %s modernize-use-equals-default %t -- -- -std=c++11 -fno-delayed-template-parsing -fexceptions + +// Out of line definition. +class OL { +public: + OL(); + ~OL(); +}; + +OL::OL() {} +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default' to define a trivial default constructor [modernize-use-equals-default] +// CHECK-FIXES: OL::OL() = default; +OL::~OL() {} +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default' to define a trivial destructor [modernize-use-equals-default] +// CHECK-FIXES: OL::~OL() = default; + +// Inline definitions. +class IL { +public: + IL() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: IL() = default; + ~IL() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: ~IL() = default; +}; + +// Non-empty body. +void f(); +class NE { +public: + NE() { f(); } + ~NE() { f(); } +}; + +// Initializer or arguments. +class IA { +public: + // Constructor with initializer. + IA() : Field(5) {} + // Constructor with arguments. + IA(int Arg1, int Arg2) {} + int Field; +}; + +// Default member initializer +class DMI { +public: + DMI() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: DMI() = default; + int Field = 5; +}; + +// Class member +class CM { +public: + CM() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: CM() = default; + OL o; +}; + +// Private constructor/destructor. +class Priv { + Priv() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: Priv() = default; + ~Priv() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: ~Priv() = default; +}; + +// struct. +struct ST { + ST() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: ST() = default; + ~ST() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: ST() = default; +}; + +// Deleted constructor/destructor. +class Del { +public: + Del() = delete; + ~Del() = delete; +}; + +// Do not remove other keywords. +class KW { +public: + explicit KW() {} + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use '= default' + // CHECK-FIXES: explicit KW() = default; + virtual ~KW() {} + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use '= default' + // CHECK-FIXES: virtual ~KW() = default; +}; + +// Nested class. +struct N { + struct NN { + NN() {} + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default' + // CHECK-FIXES: NN() = default; + ~NN() {} + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default' + // CHECK-FIXES: ~NN() = default; + }; + int Int; +}; + +// Class template. +template +class Temp { +public: + Temp() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: Temp() = default; + ~Temp() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: ~Temp() = default; +}; + +// Class template out of line with explicit instantiation. +template +class TempODef { +public: + TempODef(); + ~TempODef(); +}; + +template +TempODef::TempODef() {} +// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use '= default' +// CHECK-FIXES: TempODef::TempODef() = default; +template +TempODef::~TempODef() {} +// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use '= default' +// CHECK-FIXES: TempODef::~TempODef() = default; + +template class TempODef; +template class TempODef; + +// Non user-provided constructor/destructor. +struct Imp { + int Int; +}; +void g() { + Imp *PtrImp = new Imp(); + PtrImp->~Imp(); + delete PtrImp; +} + +// Already using default. +struct IDef { + IDef() = default; + ~IDef() = default; +}; +struct ODef { + ODef(); + ~ODef(); +}; +ODef::ODef() = default; +ODef::~ODef() = default; + +// Delegating constructor and overriden destructor. +struct DC : KW { + DC() : KW() {} + ~DC() override {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: ~DC() override = default; +}; + +struct Comments { + Comments() { + // Don't erase comments inside the body. + } + // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use '= default' + ~Comments() { + // Don't erase comments inside the body. + } + // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use '= default' +}; + +// Try-catch. +struct ITC { + ITC() try {} catch(...) {} + ~ITC() try {} catch(...) {} +}; + +struct OTC { + OTC(); + ~OTC(); +}; +OTC::OTC() try {} catch(...) {} +OTC::~OTC() try {} catch(...) {} + +#define STRUCT_WITH_DEFAULT(_base, _type) \ + struct _type { \ + _type() {} \ + _base value; \ + }; + +STRUCT_WITH_DEFAULT(unsigned char, Hex8Default) +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default' to define a trivial default constructor +// CHECK-MESSAGES: :[[@LINE-6]]:13: note: