[clang-tools-extra] [clang-tidy] Add `modernize-use-uniform-initializer` check (PR #91124)

via cfe-commits cfe-commits at lists.llvm.org
Sun May 5 07:55:05 PDT 2024


https://github.com/AMS21 updated https://github.com/llvm/llvm-project/pull/91124

>From 369bb3fe0b47872a06adb89e0414a56c26904e2c Mon Sep 17 00:00:00 2001
From: AMS21 <AMS21.github at gmail.com>
Date: Tue, 16 Apr 2024 08:56:13 +0200
Subject: [PATCH] [clang-tidy] Add `modernize-use-uniform-initializer` check

Finds usage of C-Style initialization that can be rewritten with
C++-11 uniform initializers.
---
 .../clang-tidy/modernize/CMakeLists.txt       |   1 +
 .../modernize/ModernizeTidyModule.cpp         |   3 +
 .../modernize/UseUniformInitializerCheck.cpp  | 320 ++++++++++++++++++
 .../modernize/UseUniformInitializerCheck.h    |  37 ++
 .../clang-tidy/utils/LexerUtils.cpp           |  23 ++
 .../clang-tidy/utils/LexerUtils.h             |   4 +
 clang-tools-extra/docs/ReleaseNotes.rst       |   6 +
 .../docs/clang-tidy/checks/list.rst           |   1 +
 .../modernize/use-uniform-initializer.rst     |  34 ++
 .../modernize/use-uniform-initializer.cpp     | 174 ++++++++++
 10 files changed, 603 insertions(+)
 create mode 100644 clang-tools-extra/clang-tidy/modernize/UseUniformInitializerCheck.cpp
 create mode 100644 clang-tools-extra/clang-tidy/modernize/UseUniformInitializerCheck.h
 create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/use-uniform-initializer.rst
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-uniform-initializer.cpp

diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index 8005d6e91c060c..ee88eeda2eaa32 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -46,6 +46,7 @@ add_clang_library(clangTidyModernizeModule
   UseTrailingReturnTypeCheck.cpp
   UseTransparentFunctorsCheck.cpp
   UseUncaughtExceptionsCheck.cpp
+  UseUniformInitializerCheck.cpp
   UseUsingCheck.cpp
 
   LINK_LIBS
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index 776558433c5baa..ecb88b0bb0fda5 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -47,6 +47,7 @@
 #include "UseTrailingReturnTypeCheck.h"
 #include "UseTransparentFunctorsCheck.h"
 #include "UseUncaughtExceptionsCheck.h"
+#include "UseUniformInitializerCheck.h"
 #include "UseUsingCheck.h"
 
 using namespace clang::ast_matchers;
@@ -117,6 +118,8 @@ class ModernizeModule : public ClangTidyModule {
         "modernize-use-transparent-functors");
     CheckFactories.registerCheck<UseUncaughtExceptionsCheck>(
         "modernize-use-uncaught-exceptions");
+    CheckFactories.registerCheck<UseUniformInitializerCheck>(
+        "modernize-use-uniform-initializer");
     CheckFactories.registerCheck<UseUsingCheck>("modernize-use-using");
   }
 };
diff --git a/clang-tools-extra/clang-tidy/modernize/UseUniformInitializerCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseUniformInitializerCheck.cpp
new file mode 100644
index 00000000000000..204a42d8c81895
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseUniformInitializerCheck.cpp
@@ -0,0 +1,320 @@
+//===--- UseUniformInitializerCheck.cpp - clang-tidy ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseUniformInitializerCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/Tooling/FixIt.h"
+
+AST_MATCHER(clang::VarDecl, isVarOldStyleInitializer) {
+  // If it doesn't have any initializer the initializer style is not defined.
+  if (!Node.hasInit())
+    return false;
+
+  const clang::VarDecl::InitializationStyle InitStyle = Node.getInitStyle();
+
+  return InitStyle == clang::VarDecl::InitializationStyle::CInit ||
+         InitStyle == clang::VarDecl::InitializationStyle::CallInit;
+}
+
+AST_MATCHER(clang::FieldDecl, isFieldOldStyleInitializer) {
+  // If it doesn't have any initializer the initializer style is not defined.
+  if (!Node.hasInClassInitializer() || Node.getInClassInitializer() == nullptr)
+    return false;
+
+  const clang::InClassInitStyle InitStyle = Node.getInClassInitStyle();
+
+  return InitStyle == clang::InClassInitStyle::ICIS_CopyInit;
+}
+
+AST_MATCHER(clang::CXXCtorInitializer, isCStyleInitializer) {
+  const clang::Expr *Init = Node.getInit();
+  if (Init == nullptr)
+    return false;
+
+  return !llvm::isa<clang::InitListExpr>(Init);
+}
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+
+namespace {
+
+constexpr const StringRef VarDeclID = "VarDecl";
+constexpr const StringRef FieldDeclId = "FieldDecl";
+constexpr const StringRef CtorInitID = "CtorInit";
+
+constexpr const StringRef CStyleWarningMessage =
+    "Use uniform initializer instead of C-style initializer";
+
+constexpr StringRef
+getInitializerStyleName(VarDecl::InitializationStyle InitStyle) {
+  switch (InitStyle) {
+  case VarDecl::InitializationStyle::CInit:
+    return "C";
+
+  case VarDecl::InitializationStyle::CallInit:
+    return "call";
+
+  default:
+    llvm_unreachable("Invalid initializer style!");
+  }
+}
+
+SourceRange getParenLocationsForCallInit(const Expr *InitExpr,
+                                         const SourceManager &SM,
+                                         const LangOptions &LangOpts) {
+  // We need to handle 'CXXConstructExpr' differently
+  if (isa<CXXConstructExpr>(InitExpr))
+    return cast<CXXConstructExpr>(InitExpr)->getParenOrBraceRange();
+
+  // If the init expression itself is a 'ParenExpr' then
+  // 'InitExpr->getBeginLoc()' will already point to a '(' which is not the
+  // opening paren of the 'CallInit' expression. So it that case we need to
+  // start one character before that.
+  const bool NeedOffsetForOpenParen = [&]() {
+    if (!isa<ParenExpr>(InitExpr))
+      return false;
+
+    const clang::StringRef CharBeforeParenExpr =
+        Lexer::getSourceText(CharSourceRange::getCharRange(
+                                 InitExpr->getBeginLoc().getLocWithOffset(-1),
+                                 InitExpr->getBeginLoc()),
+                             SM, LangOpts);
+
+    return llvm::isSpace(CharBeforeParenExpr[0]);
+  }();
+
+  const SourceLocation OpenParenLoc = utils::lexer::findPreviousTokenKind(
+      NeedOffsetForOpenParen ? InitExpr->getBeginLoc().getLocWithOffset(-1)
+                             : InitExpr->getBeginLoc(),
+      SM, LangOpts, tok::l_paren);
+  const SourceLocation CloseParenLoc = utils::lexer::findNextTokenKind(
+      InitExpr->getEndLoc(), SM, LangOpts, tok::r_paren);
+
+  return {OpenParenLoc, CloseParenLoc};
+}
+
+const BuiltinType *getBuiltinType(const Expr *Expr) {
+  assert(Expr);
+  return Expr->getType().getCanonicalType().getTypePtr()->getAs<BuiltinType>();
+}
+
+bool castRequiresStaticCast(const ImplicitCastExpr *CastExpr) {
+  const auto *FromExpr = CastExpr->getSubExpr();
+
+  if (CastExpr->isInstantiationDependent() ||
+      FromExpr->isInstantiationDependent())
+    return false;
+  if (getBuiltinType(CastExpr) == getBuiltinType(FromExpr))
+    return false;
+
+  switch (CastExpr->getCastKind()) {
+  case CastKind::CK_BaseToDerived:
+  case CastKind::CK_DerivedToBaseMemberPointer:
+  case CastKind::CK_IntegralCast:
+  case CastKind::CK_FloatingToIntegral:
+    return true;
+
+  default:
+    return false;
+  }
+}
+
+std::string buildReplacementString(const Expr *InitExpr,
+                                   const ASTContext &Context) {
+  // TODO: This function does not correctly handle the case where you have in
+  // 'ImplicitCastExpr' as an argument for a 'CXXConstructExpr'.
+  // In that case the generated code will not compile due to missing explicit
+  // cast of the sub expression.
+
+  const SourceManager &SM = Context.getSourceManager();
+  const LangOptions &LangOpts = Context.getLangOpts();
+
+  const StringRef InitExprStr = [&]() {
+    if (isa<CXXConstructExpr>(InitExpr)) {
+      const auto *ConstructExpr = llvm::cast<CXXConstructExpr>(InitExpr);
+
+      const SourceRange ParenRange = ConstructExpr->getParenOrBraceRange();
+      if (ParenRange.isValid())
+        return Lexer::getSourceText(
+                   CharSourceRange::getCharRange(
+                       ParenRange.getBegin().getLocWithOffset(1),
+                       ParenRange.getEnd()),
+                   SM, LangOpts)
+            .trim();
+
+      // In case the ParenRange is invalid we use Begin/EndLoc
+      const SourceLocation BeginLocation = ConstructExpr->getBeginLoc();
+      const SourceLocation EndLocation = ConstructExpr->getEndLoc();
+
+      return Lexer::getSourceText(
+                 CharSourceRange::getCharRange(BeginLocation,
+                                               EndLocation.getLocWithOffset(1)),
+                 SM, LangOpts)
+          .trim();
+    }
+
+    return tooling::fixit::getText(*InitExpr, Context);
+  }();
+
+  // For some 'ImplicitCastExpr' we need to add an extra 'static_cast<T>' around
+  // the expression since implicit conversions are not allowed with uniform
+  // initializer and otherwise will lead to compile errors
+  if (isa<ImplicitCastExpr>(InitExpr)) {
+    const auto *CastExpr = llvm::cast<ImplicitCastExpr>(InitExpr);
+
+    if (castRequiresStaticCast(CastExpr)) {
+      const QualType Type = CastExpr->getType().getUnqualifiedType();
+
+      return ("{static_cast<" + Type.getAsString() + ">(" + InitExprStr + ")}")
+          .str();
+    }
+  }
+
+  // Otherwise just add the braces around the expression
+  return ("{" + InitExprStr + "}").str();
+}
+
+} // namespace
+
+void UseUniformInitializerCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(varDecl(isVarOldStyleInitializer(), unless(parmVarDecl()))
+                         .bind(VarDeclID),
+                     this);
+  Finder->addMatcher(fieldDecl(isFieldOldStyleInitializer()).bind(FieldDeclId),
+                     this);
+  Finder->addMatcher(cxxCtorInitializer(isCStyleInitializer()).bind(CtorInitID),
+                     this);
+}
+
+void UseUniformInitializerCheck::check(const MatchFinder::MatchResult &Result) {
+  const SourceManager &SM = *Result.SourceManager;
+  const ASTContext &Context = *Result.Context;
+  const LangOptions &LangOpts = Context.getLangOpts();
+
+  const auto *VDecl = Result.Nodes.getNodeAs<VarDecl>(VarDeclID);
+  const auto *FDecl = Result.Nodes.getNodeAs<FieldDecl>(FieldDeclId);
+  const auto *CtorInit = Result.Nodes.getNodeAs<CXXCtorInitializer>(CtorInitID);
+
+  // Handle variable declarations
+  if (VDecl != nullptr) {
+    const auto *InitExpr = VDecl->getInit();
+    assert(InitExpr);
+
+    // If the expression is an 'ExprWithCleanups' just get the subexpression
+    if (isa<ExprWithCleanups>(InitExpr))
+      InitExpr = llvm::cast<ExprWithCleanups>(InitExpr)->getSubExpr();
+
+    const VarDecl::InitializationStyle InitStyle = VDecl->getInitStyle();
+
+    // Check for the special case that a default constructor with no arguments
+    // is used
+    // Example: struct A{}; A a;
+    const auto *ConstructorExpr = dyn_cast_or_null<CXXConstructExpr>(InitExpr);
+    if (ConstructorExpr && ConstructorExpr->getNumArgs() == 0)
+      return;
+
+    // Ignore declarations inside a 'CXXForRangeStmt'
+    if (VDecl->isCXXForRangeDecl())
+      return;
+
+    if (InitStyle == VarDecl::InitializationStyle::CInit) {
+      const SourceLocation EqualLoc = utils::lexer::findPreviousTokenKind(
+          InitExpr->getBeginLoc(), SM, LangOpts, tok::equal);
+      if (!EqualLoc.isValid())
+        return;
+
+      auto Diag =
+          diag(EqualLoc,
+               "Use uniform initializer instead of %0-style initializer")
+          << getInitializerStyleName(InitStyle);
+
+      // If we have a C-style initializer with an 'InitListExpr', we only need
+      // to remove the '='
+      // For example: 'int a = {0};'
+      if (isa<InitListExpr>(InitExpr))
+        Diag << FixItHint::CreateRemoval(EqualLoc);
+      else {
+        // Otherwise with a normal c-style initializer we also need to add '{'
+        // and '}'
+        // For example: 'int a = 0;'
+        const std::string ReplacementText =
+            buildReplacementString(InitExpr, Context);
+
+        Diag << FixItHint::CreateReplacement(
+            SourceRange{EqualLoc, InitExpr->getEndLoc()}, ReplacementText);
+      }
+    } else if (InitStyle == VarDecl::InitializationStyle::CallInit) {
+      const SourceRange ParenRange =
+          getParenLocationsForCallInit(InitExpr, SM, LangOpts);
+      if (ParenRange.isInvalid())
+        return;
+
+      const std::string ReplacementString =
+          buildReplacementString(InitExpr, Context);
+
+      diag(ParenRange.getBegin(),
+           "Use uniform initializer instead of %0-style initializer")
+          << getInitializerStyleName(InitStyle)
+          << FixItHint::CreateReplacement(ParenRange, ReplacementString);
+    } else {
+      llvm_unreachable("Invalid initializer style!");
+    }
+  }
+  // Handle field declarations
+  else if (FDecl != nullptr) {
+    const auto *InitExpr = FDecl->getInClassInitializer();
+    assert(InitExpr);
+
+    const SourceLocation EqualLoc = utils::lexer::findPreviousTokenKind(
+        InitExpr->getBeginLoc(), SM, LangOpts, tok::equal);
+    if (!EqualLoc.isValid())
+      return;
+
+    if (isa<InitListExpr>(InitExpr))
+      // If we have a C-style initializer with an 'InitListExpr', we only need
+      // to remove the '='
+      diag(EqualLoc, CStyleWarningMessage)
+          << FixItHint::CreateRemoval(EqualLoc);
+    else {
+      // Otherwise with a normal c-style initializer we also need to add '{' and
+      // '}' and rewrite the initializer
+      const StringRef InitExprStr = tooling::fixit::getText(*InitExpr, Context);
+
+      const std::string ReplacementText = ("{" + InitExprStr + "}").str();
+
+      diag(EqualLoc, CStyleWarningMessage) << FixItHint::CreateReplacement(
+          SourceRange{EqualLoc, InitExpr->getEndLoc()}, ReplacementText);
+    }
+  }
+  // Otherwise must be a CXXCtorInitializer
+  else {
+    assert(CtorInit != nullptr);
+
+    const auto *InitExpr = CtorInit->getInit();
+    assert(InitExpr);
+
+    const SourceLocation LParenLoc = CtorInit->getLParenLoc();
+    const SourceLocation RParenLoc = CtorInit->getRParenLoc();
+
+    if (!LParenLoc.isValid() || !RParenLoc.isValid())
+      return;
+
+    if (LParenLoc == RParenLoc)
+      return;
+
+    const std::string ReplacementString =
+        buildReplacementString(InitExpr, Context);
+
+    diag(LParenLoc, CStyleWarningMessage) << FixItHint::CreateReplacement(
+        SourceRange{LParenLoc, RParenLoc}, ReplacementString);
+  }
+}
+
+} // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/clang-tidy/modernize/UseUniformInitializerCheck.h b/clang-tools-extra/clang-tidy/modernize/UseUniformInitializerCheck.h
new file mode 100644
index 00000000000000..8263c8c3d759fc
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseUniformInitializerCheck.h
@@ -0,0 +1,37 @@
+//===--- UseUniformInitializerCheck.h - clang-tidy --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEUNIFORMINITIALIZERCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEUNIFORMINITIALIZERCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::modernize {
+
+/// Finds C-Style initializers and converts them to uniform initializers.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-uniform-initializer.html
+class UseUniformInitializerCheck : public ClangTidyCheck {
+public:
+  UseUniformInitializerCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus11;
+  }
+  std::optional<TraversalKind> getCheckTraversalKind() const override {
+    return TK_IgnoreUnlessSpelledInSource;
+  }
+
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace clang::tidy::modernize
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEUNIFORMINITIALIZERCHECK_H
diff --git a/clang-tools-extra/clang-tidy/utils/LexerUtils.cpp b/clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
index df2b0bef576ca3..88eb25e3828314 100644
--- a/clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
+++ b/clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
@@ -84,6 +84,29 @@ SourceLocation findNextTerminator(SourceLocation Start, const SourceManager &SM,
   return findNextAnyTokenKind(Start, SM, LangOpts, tok::comma, tok::semi);
 }
 
+SourceLocation findNextTokenKind(SourceLocation Start, const SourceManager &SM,
+                                 const LangOptions &LangOpts,
+                                 tok::TokenKind TK) {
+  while (true) {
+    std::optional<Token> CurrentToken =
+        Lexer::findNextToken(Start, SM, LangOpts);
+
+    if (!CurrentToken)
+      return {};
+
+    Token PotentialMatch = *CurrentToken;
+    if (PotentialMatch.is(TK))
+      return PotentialMatch.getLocation();
+
+    // If we reach the end of the file, and eof is not the target token, we stop
+    // the loop, otherwise we will get infinite loop (findNextToken will return
+    // eof on eof).
+    if (PotentialMatch.is(tok::eof))
+      return {};
+    Start = PotentialMatch.getLastLoc();
+  }
+}
+
 std::optional<Token>
 findNextTokenIncludingComments(SourceLocation Start, const SourceManager &SM,
                                const LangOptions &LangOpts) {
diff --git a/clang-tools-extra/clang-tidy/utils/LexerUtils.h b/clang-tools-extra/clang-tidy/utils/LexerUtils.h
index ea9bd512b68b8f..66e6163008a207 100644
--- a/clang-tools-extra/clang-tidy/utils/LexerUtils.h
+++ b/clang-tools-extra/clang-tidy/utils/LexerUtils.h
@@ -89,6 +89,10 @@ SourceLocation findNextAnyTokenKind(SourceLocation Start,
   }
 }
 
+SourceLocation findNextTokenKind(SourceLocation Start, const SourceManager &SM,
+                                 const LangOptions &LangOpts,
+                                 tok::TokenKind TK);
+
 std::optional<Token>
 findNextTokenIncludingComments(SourceLocation Start, const SourceManager &SM,
                                const LangOptions &LangOpts);
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 5d6d0351362e40..6d1a35b6d0b5e5 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -148,6 +148,12 @@ New checks
   Finds initializer lists for aggregate types that could be
   written as designated initializers instead.
 
+- New :doc:`modernize-use-uniform-initializer
+  <clang-tidy/checks/modernize/use-uniform-initializer>` check.
+
+  Finds usage of C-Style initialization that can be rewritten with
+  C++-11 uniform initializers.
+
 - New :doc:`readability-enum-initial-value
   <clang-tidy/checks/readability/enum-initial-value>` check.
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 046a5ff57ad1c9..5bb5b6beba5818 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -305,6 +305,7 @@ Clang-Tidy Checks
    :doc:`modernize-use-trailing-return-type <modernize/use-trailing-return-type>`, "Yes"
    :doc:`modernize-use-transparent-functors <modernize/use-transparent-functors>`, "Yes"
    :doc:`modernize-use-uncaught-exceptions <modernize/use-uncaught-exceptions>`, "Yes"
+   :doc:`modernize-use-uniform-initializer <modernize/use-uniform-initializer>`, "Yes"
    :doc:`modernize-use-using <modernize/use-using>`, "Yes"
    :doc:`mpi-buffer-deref <mpi/buffer-deref>`, "Yes"
    :doc:`mpi-type-mismatch <mpi/type-mismatch>`, "Yes"
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-uniform-initializer.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-uniform-initializer.rst
new file mode 100644
index 00000000000000..eda11e060ed2c4
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-uniform-initializer.rst
@@ -0,0 +1,34 @@
+.. title:: clang-tidy - modernize-use-uniform-initializer
+
+modernize-use-uniform-initializer
+=================================
+
+Finds usage of C-Style initialization that can be rewritten with
+C++-11 uniform initializers.
+
+Example
+-------
+
+.. code-block:: c++
+
+  int foo = 21;
+  int bar(42);
+  
+  struct Baz {
+    Baz() : x(3) {}
+
+    int x;
+  };
+
+transforms to:
+
+.. code-block:: c++
+
+  int foo{21};
+  int bar{42};
+  
+  struct Baz {
+    Baz() : x{3} {}
+
+    int x;
+  };
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-uniform-initializer.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-uniform-initializer.cpp
new file mode 100644
index 00000000000000..608af82065a935
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-uniform-initializer.cpp
@@ -0,0 +1,174 @@
+// RUN: %check_clang_tidy -std=c++11 %s modernize-use-uniform-initializer %t
+
+int cinit_0 = 0;
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: Use uniform initializer instead of C-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: int cinit_0 {0};
+
+int cinit_1=0;
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Use uniform initializer instead of C-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: int cinit_1{0};
+
+int callinit_0(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: Use uniform initializer instead of call-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: int callinit_0{0};
+
+int callinit_1 ( 0 );
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: Use uniform initializer instead of call-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: int callinit_1 {0};
+
+int callinit_2 ( ((3 + 1 + 4) + 1 + 5) );
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: Use uniform initializer instead of call-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: int callinit_2 {((3 + 1 + 4) + 1 + 5)};
+
+int callinit_3((9-2)+(6+5));
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: Use uniform initializer instead of call-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: int callinit_3{(9-2)+(6+5)};
+
+int callinit_4 ((3 * 5 + (8 - 9) ));
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: Use uniform initializer instead of call-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: int callinit_4 {(3 * 5 + (8 - 9) )};
+
+int callinit_5((7 + (-9)));
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: Use uniform initializer instead of call-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: int callinit_5{(7 + (-9))};
+
+int mixed_0 = {0};
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: Use uniform initializer instead of C-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: int mixed_0 {0};
+
+int mixed_1={0};
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Use uniform initializer instead of C-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: int mixed_1{0};
+
+int noinit;
+
+int correct_0{0};
+
+int correct_1 {0};
+
+struct CInitList_0 {
+    CInitList_0() : x(0) {}
+    // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: Use uniform initializer instead of C-style initializer [modernize-use-uniform-initializer]
+    // CHECK-FIXES: CInitList_0() : x{0} {}
+
+    int x;
+};
+
+struct CInitList_1 {
+    CInitList_1():x(0) {}
+    // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: Use uniform initializer instead of C-style initializer [modernize-use-uniform-initializer]
+    // CHECK-FIXES: CInitList_1():x{0} {}
+
+    int x;
+};
+
+struct CInitList_2 {
+    CInitList_2() : x ( 0 ) {}
+    // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: Use uniform initializer instead of C-style initializer [modernize-use-uniform-initializer]
+    // CHECK-FIXES: CInitList_2() : x {0} {}
+
+    int x;
+};
+
+struct Correct_0 {
+    Correct_0() : x{0} {}
+
+    int x;
+};
+
+struct Correct_1 {
+    Correct_1():x{0} {}
+
+    int x;
+};
+
+struct InClassInitializers {
+    int cinit_0 = 0;
+    // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: Use uniform initializer instead of C-style initializer [modernize-use-uniform-initializer]
+    // CHECK-FIXES: int cinit_0 {0};
+
+    int cinit_1=0;
+    // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: Use uniform initializer instead of C-style initializer [modernize-use-uniform-initializer]
+    // CHECK-FIXES: int cinit_1{0};
+
+    int mixed_0 = {0};
+    // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: Use uniform initializer instead of C-style initializer [modernize-use-uniform-initializer]
+    // CHECK-FIXES: int mixed_0 {0};
+
+    int mixed_1={0};
+    // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: Use uniform initializer instead of C-style initializer [modernize-use-uniform-initializer]
+    // CHECK-FIXES: int mixed_1{0};
+
+    int noinit;
+
+    int correct_0 {0};
+
+    int correct_1{0};
+};
+
+// Defaulted function arguments cannot use the uniform initializer
+void f(int x = 0) {}
+
+struct A {
+    A() = default;
+    A(int) {}
+    A(int, int) {}
+};
+
+A a_0;
+A a_1{};
+A a_2(); // This is not a variable declaration it is actually a function definition (most vexing parse)
+A a_3{21};
+A a_4{ 21 };
+A a_5(42);
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: Use uniform initializer instead of call-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: A a_5{42};
+A a_6 ( 42 );
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: Use uniform initializer instead of call-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: A a_6 {42};
+A a_7{0, 1};
+A a_8{ 0, 1 };
+A a_9(0, 1);
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: Use uniform initializer instead of call-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: A a_9{0, 1};
+A a_10( 1, 0 );
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: Use uniform initializer instead of call-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: A a_10{1, 0};
+A a_11(A(3));
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: Use uniform initializer instead of call-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: A a_11{A(3)};
+A a_12 ( A(3) );
+// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Use uniform initializer instead of call-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: A a_12 {A(3)};
+
+struct Delegating {
+    Delegating() : Delegating(0) {}
+    // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: Use uniform initializer instead of C-style initializer [modernize-use-uniform-initializer]
+    // CHECK-FIXES: Delegating() : Delegating{0} {}
+
+    Delegating(int) {}
+};
+
+int narrow_0 = 3.14;
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: Use uniform initializer instead of C-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: int narrow_0 {static_cast<int>(3.14)};
+int narrow_1=37.73;
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: Use uniform initializer instead of C-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: int narrow_1{static_cast<int>(37.73)};
+int narrow_2(0.9);
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: Use uniform initializer instead of call-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: int narrow_2{static_cast<int>(0.9)};
+int narrow_3 ( 5.666 );
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: Use uniform initializer instead of call-style initializer [modernize-use-uniform-initializer]
+// CHECK-FIXES: int narrow_3 {static_cast<int>(5.666)};
+
+template <class T> struct Vec {
+  T *begin();
+  T *end();
+};
+
+void for_each() {
+    Vec<int> v;
+    for (int& x : v)
+    {}
+}



More information about the cfe-commits mailing list