Index: clang-tidy/modernize/CMakeLists.txt =================================================================== --- clang-tidy/modernize/CMakeLists.txt +++ clang-tidy/modernize/CMakeLists.txt @@ -16,6 +16,7 @@ ShrinkToFitCheck.cpp UseAutoCheck.cpp UseBoolLiteralsCheck.cpp + UseDefaultMemberInitCheck.cpp UseEmplaceCheck.cpp UseEqualsDefaultCheck.cpp UseEqualsDeleteCheck.cpp Index: clang-tidy/modernize/ModernizeTidyModule.cpp =================================================================== --- clang-tidy/modernize/ModernizeTidyModule.cpp +++ clang-tidy/modernize/ModernizeTidyModule.cpp @@ -22,6 +22,7 @@ #include "ShrinkToFitCheck.h" #include "UseAutoCheck.h" #include "UseBoolLiteralsCheck.h" +#include "UseDefaultMemberInitCheck.h" #include "UseEmplaceCheck.h" #include "UseEqualsDefaultCheck.h" #include "UseEqualsDeleteCheck.h" @@ -56,6 +57,8 @@ CheckFactories.registerCheck("modernize-use-auto"); CheckFactories.registerCheck( "modernize-use-bool-literals"); + CheckFactories.registerCheck( + "modernize-use-default-member-init"); CheckFactories.registerCheck("modernize-use-emplace"); CheckFactories.registerCheck("modernize-use-equals-default"); CheckFactories.registerCheck( Index: clang-tidy/modernize/UseDefaultMemberInitCheck.h =================================================================== --- /dev/null +++ clang-tidy/modernize/UseDefaultMemberInitCheck.h @@ -0,0 +1,45 @@ +//===--- UseDefaultMemberInitCheck.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_MEMBER_INIT_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_MEMBER_INIT_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace modernize { + +/// Convert a default constructor's member initializers into default member +/// initializers. Remove member initializers that are the same as a default +/// member initializer. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-default-member-init.html +class UseDefaultMemberInitCheck : public ClangTidyCheck { +public: + UseDefaultMemberInitCheck(StringRef Name, ClangTidyContext *Context); + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + void checkDefaultInit(const ast_matchers::MatchFinder::MatchResult &Result, + const CXXCtorInitializer *Init); + void checkExistingInit(const ast_matchers::MatchFinder::MatchResult &Result, + const CXXCtorInitializer *Init); + + const bool UseAssignment; +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_MEMBER_INIT_H Index: clang-tidy/modernize/UseDefaultMemberInitCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/modernize/UseDefaultMemberInitCheck.cpp @@ -0,0 +1,207 @@ +//===--- UseDefaultMemberInitCheck.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 "UseDefaultMemberInitCheck.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 StringRef getValueInit(const CXXCtorInitializer *Init) { + switch (Init->getInit()->getType()->getScalarTypeKind()) { + case Type::STK_CPointer: + case Type::STK_BlockPointer: + case Type::STK_ObjCObjectPointer: + case Type::STK_MemberPointer: + return "nullptr"; + case Type::STK_Bool: + return "false"; + case Type::STK_Integral: + return "0"; + case Type::STK_Floating: + case Type::STK_IntegralComplex: + case Type::STK_FloatingComplex: + return "0.0"; + } + llvm_unreachable("Invalid scalar type kind"); +} + +static bool isZero(const Expr *E) { + switch (E->getStmtClass()) { + case Stmt::CXXNullPtrLiteralExprClass: + case Stmt::ImplicitValueInitExprClass: + return true; + case Stmt::InitListExprClass: + return cast(E)->getNumInits() == 0; + case Stmt::CXXBoolLiteralExprClass: + return !cast(E)->getValue(); + case Stmt::IntegerLiteralClass: + return !cast(E)->getValue(); + case Stmt::FloatingLiteralClass: { + llvm::APFloat Value = cast(E)->getValue(); + return Value.isZero() && !Value.isNegative(); + } + default: + return false; + } +} + +static const Expr *ignoreUnaryPlus(const Expr *E) { + auto *UnaryOp = dyn_cast(E); + if (UnaryOp && UnaryOp->getOpcode() == UO_Plus) { + return UnaryOp->getSubExpr(); + } + return E; +} + +static const Expr *getInitializer(const Expr *E) { + auto *InitList = dyn_cast(E); + if (InitList && InitList->getNumInits() == 1) { + return InitList->getInit(0); + } + return E; +} + +static bool sameValue(const Expr *E1, const Expr *E2) { + E1 = ignoreUnaryPlus(getInitializer(E1->IgnoreImpCasts())); + E2 = ignoreUnaryPlus(getInitializer(E2->IgnoreImpCasts())); + + if (isZero(E1) && isZero(E2)) { + return true; + } + + if (E1->getStmtClass() != E2->getStmtClass()) { + return false; + } + + switch (E1->getStmtClass()) { + case Stmt::UnaryOperatorClass: + return sameValue(cast(E1)->getSubExpr(), + cast(E2)->getSubExpr()); + case Stmt::CXXBoolLiteralExprClass: + return cast(E1)->getValue() == + cast(E2)->getValue(); + case Stmt::IntegerLiteralClass: + return cast(E1)->getValue() == + cast(E2)->getValue(); + case Stmt::FloatingLiteralClass: + return cast(E1)->getValue().bitwiseIsEqual( + cast(E2)->getValue()); + case Stmt::DeclRefExprClass: + return cast(E1)->getDecl() == cast(E2)->getDecl(); + default: + break; + } + return false; +} + +UseDefaultMemberInitCheck::UseDefaultMemberInitCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + UseAssignment(Options.get("UseAssignment", 0) != 0) {} + +void UseDefaultMemberInitCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "UseAssignment", UseAssignment); +} + +AST_MATCHER(FieldDecl, hasInClassInitializer) { + return Node.hasInClassInitializer(); +} + +void UseDefaultMemberInitCheck::registerMatchers(MatchFinder *Finder) { + auto Init = + anyOf(integerLiteral(), + unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")), + has(integerLiteral())), + floatLiteral(), + unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")), + has(floatLiteral())), + cxxBoolLiteral(), cxxNullPtrLiteralExpr(), implicitValueInitExpr(), + declRefExpr()); + + Finder->addMatcher( + cxxConstructorDecl( + isDefaultConstructor(), + unless(ast_matchers::isTemplateInstantiation()), + forEachConstructorInitializer(allOf( + forField(unless(anyOf(isBitField(), hasInClassInitializer()))), + cxxCtorInitializer(isWritten(), + withInitializer(ignoringImplicit(Init))) + .bind("default")))), + this); + + Finder->addMatcher( + cxxConstructorDecl( + unless(ast_matchers::isTemplateInstantiation()), + forEachConstructorInitializer( + allOf(forField(hasInClassInitializer()), + cxxCtorInitializer(isWritten(), + withInitializer(ignoringImplicit(Init))) + .bind("existing")))), + this); +} + +void UseDefaultMemberInitCheck::check(const MatchFinder::MatchResult &Result) { + const auto *Default = Result.Nodes.getNodeAs("default"); + const auto *Existing = Result.Nodes.getNodeAs("existing"); + + if (Default) + checkDefaultInit(Result, Default); + else + checkExistingInit(Result, Existing); +} + +void UseDefaultMemberInitCheck::checkDefaultInit( + const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) { + const FieldDecl *Field = Init->getMember(); + + SourceLocation FieldEnd = + Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0, + *Result.SourceManager, getLangOpts()); + SourceLocation LParenEnd = Lexer::getLocForEndOfToken( + Init->getLParenLoc(), 0, *Result.SourceManager, getLangOpts()); + CharSourceRange InitRange = + CharSourceRange::getCharRange(LParenEnd, Init->getRParenLoc()); + + auto Diag = + diag(Field->getLocation(), "use default member initializer for %0") + << Field + << FixItHint::CreateInsertion(FieldEnd, UseAssignment ? " = " : "{") + << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange); + + if (UseAssignment && isa(Init->getInit())) + Diag << FixItHint::CreateInsertion(FieldEnd, getValueInit(Init)); + + if (!UseAssignment) + Diag << FixItHint::CreateInsertion(FieldEnd, "}"); + + Diag << FixItHint::CreateRemoval(Init->getSourceRange()); +} + +void UseDefaultMemberInitCheck::checkExistingInit( + const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) { + const FieldDecl *Field = Init->getMember(); + + if (!sameValue(Field->getInClassInitializer(), Init->getInit())) + return; + + diag(Init->getSourceLocation(), "member initializer for %0 is redundant") + << Field + << FixItHint::CreateRemoval(Init->getSourceRange()); +} + +} // namespace modernize +} // namespace tidy +} // namespace clang Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -93,6 +93,12 @@ - The modernize-use-default check has been renamed to `modernize-use-equals-default `_. + +- New `modernize-use-default-member-init + `_ check + + Converts a default constructor's member initializers into default member initializers. + Removes member initializers that are the same as a default member initializer. - New `modernize-use-equals-delete `_ check Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -109,6 +109,7 @@ modernize-shrink-to-fit modernize-use-auto modernize-use-bool-literals + modernize-use-default-member-init modernize-use-emplace modernize-use-equals-default modernize-use-equals-delete Index: docs/clang-tidy/checks/modernize-use-default-member-init.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/modernize-use-default-member-init.rst @@ -0,0 +1,49 @@ +.. title:: clang-tidy - modernize-use-default-member-init + +modernize-use-default-member-init +================================= + +This check converts a default constructor's member initializers into default +member initializers. Other member initializers that match the default +member initializer are removed. This can reduce repeated code or allow use of +'= default'. + +.. code-block:: c++ + + struct A { + A() : i(5), j(10.0) {} + A(int i) : i(i), j(10.0) {} + int i; + double j; + }; + + // becomes + + struct A { + A() {} + A(int i) : i(i) {} + int i{5}; + double j{10.0}; + }; + +.. note:: + Only converts member initializers for built-in types, enums and pointers. + The `readability-redundant-member-init` check will remove redundant member + initializers for classes. + +Options +------- + +.. option:: UseAssignment + + If this option is set to non-zero (default is `0`), the check will initialise + members with an assignment. For example: + +.. code-block:: c++ + + struct A { + A() {} + A(int i) : i(i) {} + int i = 5; + double j = 10.0; + }; Index: test/clang-tidy/modernize-use-default-member-init-assignment.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-use-default-member-init-assignment.cpp @@ -0,0 +1,87 @@ +// RUN: %check_clang_tidy %s modernize-use-default-member-init %t -- \ +// RUN: -config="{CheckOptions: [{key: modernize-use-default-member-init.UseAssignment, value: 1}]}" -- -std=c++11 + +struct S { +}; + +struct PositiveValueInt { + PositiveValueInt() : i() {} + // CHECK-FIXES: PositiveValueInt() {} + const int i; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use default member initializer for 'i' [modernize-use-default-member-init] + // CHECK-FIXES: const int i = 0; +}; + +struct PositiveInt { + PositiveInt() : j(1) {} + // CHECK-FIXES: PositiveInt() {} + int j; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'j' [modernize-use-default-member-init] + // CHECK-FIXES: int j = 1; +}; + +struct PositiveValueDouble { + PositiveValueDouble() : d() {} + // CHECK-FIXES: PositiveValueDouble() {} + double d; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'd' [modernize-use-default-member-init] + // CHECK-FIXES: double d = 0.0; +}; + +struct PositiveDouble { + PositiveDouble() : f(2.5463e43) {} + // CHECK-FIXES: PositiveDouble() {} + double f; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'f' [modernize-use-default-member-init] + // CHECK-FIXES: double f = 2.5463e43; +}; + +struct PositiveValueBool { + PositiveValueBool() : b() {} + // CHECK-FIXES: PositiveValueBool() {} + bool b; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'b' [modernize-use-default-member-init] + // CHECK-FIXES: bool b = false; +}; + +struct PositiveBool { + PositiveBool() : a(true) {} + // CHECK-FIXES: PositiveBool() {} + bool a; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'a' [modernize-use-default-member-init] + // CHECK-FIXES: bool a = true; +}; + +struct PositiveValuePointer { + PositiveValuePointer() : p() {} + // CHECK-FIXES: PositiveValuePointer() {} + int *p; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'p' [modernize-use-default-member-init] + // CHECK-FIXES: int *p = nullptr; +}; + +struct PositiveNullPointer { + PositiveNullPointer() : q(nullptr) {} + // CHECK-FIXES: PositiveNullPointer() {} + int *q; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'q' [modernize-use-default-member-init] + // CHECK-FIXES: int *q = nullptr; +}; + +enum Enum { Foo }; +struct PositiveEnum { + PositiveEnum() : e(Foo) {} + // CHECK-FIXES: PositiveEnum() {} + Enum e; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'e' [modernize-use-default-member-init] + // CHECK-FIXES: Enum e = Foo; +}; + +template +struct NegativeTemplate { + NegativeTemplate() : t() {} + T t; +}; + +NegativeTemplate nti; +NegativeTemplate ntd; Index: test/clang-tidy/modernize-use-default-member-init.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-use-default-member-init.cpp @@ -0,0 +1,264 @@ +// RUN: %check_clang_tidy %s modernize-use-default-member-init %t -- -- -std=c++11 + +struct S { +}; + +struct PositiveValueInt { + PositiveValueInt() : i() {} + // CHECK-FIXES: PositiveValueInt() {} + const int i; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use default member initializer for 'i' [modernize-use-default-member-init] + // CHECK-FIXES: const int i{}; +}; + +struct PositiveInt { + PositiveInt() : j(1) {} + // CHECK-FIXES: PositiveInt() {} + int j; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'j' + // CHECK-FIXES: int j{1}; +}; + +struct PositiveUnaryMinusInt { + PositiveUnaryMinusInt() : j(-1) {} + // CHECK-FIXES: PositiveUnaryMinusInt() {} + int j; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'j' + // CHECK-FIXES: int j{-1}; +}; + +struct PositiveUnaryPlusInt { + PositiveUnaryPlusInt() : j(+1) {} + // CHECK-FIXES: PositiveUnaryPlusInt() {} + int j; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'j' + // CHECK-FIXES: int j{+1}; +}; + +struct PositiveValueDouble { + PositiveValueDouble() : d() {} + // CHECK-FIXES: PositiveValueDouble() {} + double d; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'd' + // CHECK-FIXES: double d{}; +}; + +struct PositiveDouble { + PositiveDouble() : f(2.5463e43) {} + // CHECK-FIXES: PositiveDouble() {} + double f; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'f' + // CHECK-FIXES: double f{2.5463e43}; +}; + +struct PositiveUnaryMinusDouble { + PositiveUnaryMinusDouble() : f(-2.5463e43) {} + // CHECK-FIXES: PositiveUnaryMinusDouble() {} + double f; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'f' + // CHECK-FIXES: double f{-2.5463e43}; +}; + +struct PositiveUnaryPlusDouble { + PositiveUnaryPlusDouble() : f(+2.5463e43) {} + // CHECK-FIXES: PositiveUnaryPlusDouble() {} + double f; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'f' + // CHECK-FIXES: double f{+2.5463e43}; +}; + +struct PositiveValueBool { + PositiveValueBool() : b() {} + // CHECK-FIXES: PositiveValueBool() {} + bool b; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'b' + // CHECK-FIXES: bool b{}; +}; + +struct PositiveBool { + PositiveBool() : a(true) {} + // CHECK-FIXES: PositiveBool() {} + bool a; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'a' + // CHECK-FIXES: bool a{true}; +}; + +struct PositiveValuePointer { + PositiveValuePointer() : p() {} + // CHECK-FIXES: PositiveValuePointer() {} + int *p; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'p' + // CHECK-FIXES: int *p{}; +}; + +struct PositiveNullPointer { + PositiveNullPointer() : q(nullptr) {} + // CHECK-FIXES: PositiveNullPointer() {} + int *q; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'q' + // CHECK-FIXES: int *q{nullptr}; +}; + +enum Enum { Foo, Bar }; +struct PositiveEnum { + PositiveEnum() : e(Foo) {} + // CHECK-FIXES: PositiveEnum() {} + Enum e; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'e' + // CHECK-FIXES: Enum e{Foo}; +}; + +template +struct NegativeTemplate { + NegativeTemplate() : t() {} + T t; +}; + +NegativeTemplate nti; +NegativeTemplate ntd; + +struct NegativeDefaultMember { + NegativeDefaultMember() {} + int i = 2; +}; + +struct NegativeClass : S { + NegativeClass() : s() {} + S s; +}; + +struct NegativeBase : S { + NegativeBase() : S() {} +}; + +struct NegativeDefaultOtherMember{ + NegativeDefaultOtherMember() : i(3) {} + int i = 4; +}; + +struct NegativeUnion { + NegativeUnion() : d(5.0) {} + union { + int i; + double d; + }; +}; + +struct NegativeBitField +{ + NegativeBitField() : i(6) {} + int i : 5; +}; + +struct NegativeNotDefaultInt +{ + NegativeNotDefaultInt(int) : i(7) {} + int i; +}; + +struct ExistingInt { + ExistingInt(short) : e1(), e2(), e3(), e4(), e5(), e6() {} + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: member initializer for 'e1' is redundant [modernize-use-default-member-init] + // CHECK-MESSAGES: :[[@LINE-2]]:30: warning: member initializer for 'e2' is redundant + // CHECK-FIXES: ExistingInt(short) : e3(), e4(), e5(), e6() {} + ExistingInt(int) : e1(0), e2(0), e3(0), e4(0), e5(0), e6(0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: member initializer for 'e1' is redundant + // CHECK-MESSAGES: :[[@LINE-2]]:29: warning: member initializer for 'e2' is redundant + // CHECK-FIXES: ExistingInt(int) : e3(0), e4(0), e5(0), e6(0) {} + ExistingInt(long) : e1(5), e2(5), e3(5), e4(5), e5(5), e6(5) {} + // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: member initializer for 'e3' is redundant + // CHECK-MESSAGES: :[[@LINE-2]]:44: warning: member initializer for 'e4' is redundant + // CHECK-MESSAGES: :[[@LINE-3]]:58: warning: member initializer for 'e6' is redundant + // CHECK-FIXES: ExistingInt(long) : e1(5), e2(5), e5(5) {} + ExistingInt(char) : e1(-5), e2(-5), e3(-5), e4(-5), e5(-5), e6(-5) {} + // CHECK-MESSAGES: :[[@LINE-1]]:55: warning: member initializer for 'e5' is redundant + // CHECK-FIXES: ExistingInt(char) : e1(-5), e2(-5), e3(-5), e4(-5), e6(-5) {} + int e1{}; + int e2 = 0; + int e3 = {5}; + int e4 = 5; + int e5 = -5; + int e6 = +5; +}; + +struct ExistingDouble { + ExistingDouble(short) : e1(), e2(), e3(), e4(), e5() {} + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: member initializer for 'e1' is redundant + // CHECK-MESSAGES: :[[@LINE-2]]:33: warning: member initializer for 'e2' is redundant + // CHECK-FIXES: ExistingDouble(short) : e3(), e4(), e5() {} + ExistingDouble(int) : e1(0.0), e2(0.0), e3(0.0), e4(0.0), e5(0.0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: member initializer for 'e1' is redundant + // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: member initializer for 'e2' is redundant + // CHECK-FIXES: ExistingDouble(int) : e3(0.0), e4(0.0), e5(0.0) {} + ExistingDouble(long) : e1(5.0), e2(5.0), e3(5.0), e4(5.0), e5(5.0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: member initializer for 'e3' is redundant + // CHECK-MESSAGES: :[[@LINE-2]]:62: warning: member initializer for 'e5' is redundant + // CHECK-FIXES: ExistingDouble(long) : e1(5.0), e2(5.0), e4(5.0) {} + ExistingDouble(char) : e1(-5.0), e2(-5.0), e3(-5.0), e4(-5.0), e5(-5.0) {} + // CHECK-MESSAGES: :[[@LINE-1]]:56: warning: member initializer for 'e4' is redundant + // CHECK-FIXES: ExistingDouble(char) : e1(-5.0), e2(-5.0), e3(-5.0), e5(-5.0) {} + double e1{}; + double e2 = 0.0; + double e3 = 5.0; + double e4 = -5.0; + double e5 = +5.0; +}; + +struct ExistingBool { + ExistingBool(short) : e1(), e2(), e3() {} + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: member initializer for 'e1' is redundant + // CHECK-MESSAGES: :[[@LINE-2]]:31: warning: member initializer for 'e2' is redundant + // CHECK-FIXES: ExistingBool(short) : e3() {} + ExistingBool(int) : e1(false), e2(false), e3(false) {} + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: member initializer for 'e1' is redundant + // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: member initializer for 'e2' is redundant + // CHECK-FIXES: ExistingBool(int) : e3(false) {} + ExistingBool(long) : e1(true), e2(true), e3(true) {} + // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: member initializer for 'e3' is redundant + // CHECK-FIXES: ExistingBool(long) : e1(true), e2(true) {} + bool e1{}; + bool e2 = false; + bool e3 = true; +}; + +struct ExistingEnum { + ExistingEnum(short) : e1(Foo), e2(Foo) {} + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: member initializer for 'e1' is redundant + // CHECK-FIXES: ExistingEnum(short) : e2(Foo) {} + ExistingEnum(int) : e1(Bar), e2(Bar) {} + // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: member initializer for 'e2' is redundant + // CHECK-FIXES: ExistingEnum(int) : e1(Bar) {} + Enum e1 = Foo; + Enum e2{Bar}; +}; + +struct ExistingPointer { + ExistingPointer(short) : e1(), e2(), e3(), e4() {} + // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: member initializer for 'e1' is redundant + // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: member initializer for 'e2' is redundant + // CHECK-MESSAGES: :[[@LINE-3]]:40: warning: member initializer for 'e3' is redundant + // CHECK-FIXES: ExistingPointer(short) : e4() {} + ExistingPointer(int) : e1(0), e2(0), e3(0), e4(&e1) {} + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: member initializer for 'e1' is redundant + // CHECK-MESSAGES: :[[@LINE-2]]:33: warning: member initializer for 'e2' is redundant + // CHECK-MESSAGES: :[[@LINE-3]]:40: warning: member initializer for 'e3' is redundant + // CHECK-FIXES: ExistingPointer(int) : e4(&e1) {} + ExistingPointer(long) : e1(nullptr), e2(nullptr), e3(nullptr), e4(&e2) {} + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: member initializer for 'e1' is redundant + // CHECK-MESSAGES: :[[@LINE-2]]:40: warning: member initializer for 'e2' is redundant + // CHECK-MESSAGES: :[[@LINE-3]]:53: warning: member initializer for 'e3' is redundant + // CHECK-FIXES: ExistingPointer(long) : e4(&e2) {} + int *e1{}; + int *e2 = 0; + int *e3 = nullptr; + int **e4 = &e1; +}; + +template +struct NegativeTemplateExisting { + NegativeTemplateExisting(int) : t(0) {} + T t{}; +}; + +NegativeTemplateExisting ntei(0); +NegativeTemplateExisting nted(0);