hintonda created this revision.
hintonda added reviewers: alexfh, aaron.ballman.
hintonda added subscribers: etienneb, Eugene.Zelenko, cfe-commits.

[clang-tidy] New checker to replace dynamic exception
specifications

This is an alternative to D18575 which relied on reparsing the decl to
find the location of dynamic exception specifications, but couldn't
deal with preprocessor conditionals correctly without reparsing the
entire file.

This approach uses D20428 to find dynamic exception specifiaction
locations and handles all cases correctly.

http://reviews.llvm.org/D20693

Files:
  clang-tidy/modernize/CMakeLists.txt
  clang-tidy/modernize/ModernizeTidyModule.cpp
  clang-tidy/modernize/UseNoexceptCheck.cpp
  clang-tidy/modernize/UseNoexceptCheck.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/modernize-use-noexcept.rst
  test/clang-tidy/modernize-use-noexcept-macro.cpp
  test/clang-tidy/modernize-use-noexcept.cpp

Index: test/clang-tidy/modernize-use-noexcept.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/modernize-use-noexcept.cpp
@@ -0,0 +1,56 @@
+// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \
+// RUN:   -- -std=c++11
+
+class A {};
+class B {};
+
+void foo() throw();
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: found dynamic exception specification 'throw()' [modernize-use-noexcept]
+// CHECK-FIXES: void foo() noexcept;
+
+void bar() throw(...);
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: found dynamic exception specification 'throw(...)' [modernize-use-noexcept]
+// CHECK-FIXES: void bar() noexcept(false);
+
+void k() throw(int(int));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: found dynamic exception specification 'throw(int(int))' [modernize-use-noexcept]
+// CHECK-FIXES: void k() noexcept(false);
+
+void foobar() throw(A, B)
+{}
+// CHECK-MESSAGES: :[[@LINE-2]]:15: warning: found dynamic exception specification 'throw(A, B)' [modernize-use-noexcept]
+// CHECK-FIXES: void foobar() noexcept(false)
+
+void baz(int = (throw A(), 0)) throw(A, B) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: found dynamic exception specification 'throw(A, B)' [modernize-use-noexcept]
+// CHECK-FIXES: void baz(int = (throw A(), 0)) noexcept(false) {}
+
+void g(void (*fp)(void) throw());
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: found dynamic exception specification 'throw()' [modernize-use-noexcept]
+// CHECK-FIXES: void g(void (*fp)(void) noexcept);
+
+void f(void (*fp)(void) throw()) throw(char);
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: found dynamic exception specification 'throw()' [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-2]]:34: warning: found dynamic exception specification 'throw(char)' [modernize-use-noexcept]
+// CHECK-FIXES: void f(void (*fp)(void) noexcept) noexcept(false);
+
+void j() throw(int(int) throw(void(void) throw(int)));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: found dynamic exception specification 'throw(int(int) throw(void(void) throw(int)))' [modernize-use-noexcept]
+// CHECK-FIXES: void j() noexcept(false);
+
+class Y {
+  Y() throw() = default;
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:7: warning: found dynamic exception specification 'throw()' [modernize-use-noexcept]
+// CHECK-FIXES: Y() noexcept = default;
+
+// Should not trigger a replacement.
+void titi() noexcept {}
+void toto() noexcept(true) {}
+
+// Should not trigger a replacement.
+void bad()
+#if !__has_feature(cxx_noexcept)
+    throw()
+#endif
+        ;
Index: test/clang-tidy/modernize-use-noexcept-macro.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/modernize-use-noexcept-macro.cpp
@@ -0,0 +1,24 @@
+// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \
+// RUN:   -config="{CheckOptions: [{key: modernize-use-noexcept.ReplacementString, value: 'NOEXCEPT'}]}" \
+// RUN:   -- -std=c++11
+
+// Example definition of NOEXCEPT -- simplified test to see if noexcept is supported. 
+#if (__has_feature(cxx_noexcept))
+#define NOEXCEPT noexcept
+#else
+#define NOEXCEPT throw()
+#endif
+
+void bar() throw() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: found dynamic exception specification 'throw()' [modernize-use-noexcept]
+// CHECK-FIXES: void bar() NOEXCEPT {}
+
+// Should not trigger a FixItHint, since macros only support noexcept, and this
+// case throws.
+class A {};
+class B {};
+void foobar() throw(A, B);
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: found dynamic exception specification 'throw(A, B)' [modernize-use-noexcept]
+
+// Should not trigger a replacement.
+void foo() noexcept(true);
Index: docs/clang-tidy/checks/modernize-use-noexcept.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/modernize-use-noexcept.rst
@@ -0,0 +1,55 @@
+.. title:: clang-tidy - modernize-use-noexcept
+
+modernize-use-noexcept
+======================
+
+The check converts dynamic exception specifications, e.g., ``throw()``,
+``throw(<exception>[,...])``, or ``throw(...)`` to ``noexcept``, ``noexcept(false)``,
+or a user defined macro.
+
+Example
+-------
+
+.. code-block:: c++
+
+  void foo() throw();
+	void bar() throw(int) {}
+
+transforms to:
+
+.. code-block:: c++
+
+  void foo() noexcept;
+	void bar() noexcept(false) {}
+
+
+User defined macros
+-------------------
+
+By default this check will only replace ``throw()`` with ``noexcept``,
+and ``throw(<exception>[,...])`` or ``throw(...)`` with
+``noexcept(false)``.  Additionally, users can also use
+:option:`ReplacementString` to specify a macro to use instead of
+``noexcept``.  This is useful when maintaining source code that must
+be compiled with older compilers that don't support the ``noexcept``
+keyword.  Users can define the macro to be ``noexcept`` or ``throw()``
+depending on whether or not noexcept is supported.
+
+Please note that since ``throw(int)`` is equivelent to
+``noexcept(false)`` not ``noexcept``, this check will detect, but not
+provide a FixItHint in that case.
+
+Example
+^^^^^^^
+
+.. code-block:: c++
+
+  void foo() throw() {}
+
+transforms to:
+
+.. code-block:: c++
+
+  void foo() NOEXCEPT {}
+
+if the :option:`ReplacementString` option is set to `NOEXCEPT`.
Index: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -206,6 +206,11 @@
 
   Finds integer literals which are cast to ``bool``.
 
+- New `modernize-use-noexcept
+  <http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-noexcept.html>`_ check
+
+  Replaces dynamic exception specifications with ``noexcept`` or a user defined macro.
+
 - New `performance-faster-string-find
   <http://clang.llvm.org/extra/clang-tidy/checks/performance-faster-string-find.html>`_ check
 
Index: clang-tidy/modernize/UseNoexceptCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/modernize/UseNoexceptCheck.h
@@ -0,0 +1,50 @@
+//===--- UseNoexceptCheck.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_NOEXCEPT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_NOEXCEPT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+using CandidateSet = llvm::StringSet<llvm::MallocAllocator>;
+
+/// \brief Replace dynamic exception specifications, with
+/// `noexcept` (or user-defined macro) or `noexcept(false)`.
+/// \code
+///   void foo() throw();
+///   void bar() throw(int);
+/// \endcode
+/// Is converted to:
+/// \code
+///   void foo() noexcept;
+//    void bar() noexcept(false);
+/// \endcode
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-noexcept.html
+class UseNoexceptCheck : public ClangTidyCheck {
+public:
+  UseNoexceptCheck(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:
+  const std::string DefaultReplacement;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_NOEXCEPT_H
Index: clang-tidy/modernize/UseNoexceptCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/modernize/UseNoexceptCheck.cpp
@@ -0,0 +1,102 @@
+//===--- UseNoexceptCheck.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 "UseNoexceptCheck.h"
+#include "clang/AST/ASTContext.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+static StringRef
+makeDynamicExceptionString(const SourceManager &SM,
+                           const CharSourceRange &FileMoveRange) {
+  std::pair<FileID, unsigned> BeginInfo =
+      SM.getDecomposedLoc(FileMoveRange.getBegin());
+  std::pair<FileID, unsigned> EndInfo =
+      SM.getDecomposedLoc(FileMoveRange.getEnd());
+
+  // Add 1 which represents the size of the trailing ')'.
+  auto Len = EndInfo.second - BeginInfo.second + 1;
+  return StringRef(SM.getCharacterData(FileMoveRange.getBegin()), Len);
+}
+
+UseNoexceptCheck::UseNoexceptCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      DefaultReplacement(Options.get("ReplacementString", "noexcept")) {}
+
+void UseNoexceptCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "ReplacementString", DefaultReplacement);
+}
+
+void UseNoexceptCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(
+      functionDecl(hasTypeLoc(loc(functionType(hasDynamicExceptionSpec()))))
+          .bind("funcDecl"),
+      this);
+  Finder->addMatcher(
+      parmVarDecl(hasType(pointerType(pointee(parenType(
+                      innerType(functionType(hasDynamicExceptionSpec())))))))
+          .bind("parmVarDecl"),
+      this);
+}
+
+void UseNoexceptCheck::check(const MatchFinder::MatchResult &Result) {
+  bool IsNoThrow = false;
+  SourceRange Range;
+
+  if (const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl")) {
+    if (auto const *FnTy = FuncDecl->getType()->getAs<FunctionProtoType>())
+      IsNoThrow = FnTy->isNothrow(*Result.Context);
+    if (const auto *Info = FuncDecl->getTypeSourceInfo())
+      Range =
+          Info->getTypeLoc().castAs<FunctionTypeLoc>().getExceptionSpecRange();
+  }
+  if (const auto *ParmDecl =
+          Result.Nodes.getNodeAs<ParmVarDecl>("parmVarDecl")) {
+    if (const auto *PtrTy = ParmDecl->getType()->getAs<PointerType>())
+      if (const auto *FnTy =
+              PtrTy->getPointeeType()->getAs<FunctionProtoType>())
+        IsNoThrow = FnTy->isNothrow(*Result.Context);
+
+    if (const TypeSourceInfo *TSI = ParmDecl->getTypeSourceInfo()) {
+      TypeLoc TL = TSI->getTypeLoc();
+      if (TL.getType()->isPointerType()) {
+        TL = TL.getNextTypeLoc().IgnoreParens();
+        if (auto FPTL = TL.getAs<FunctionProtoTypeLoc>()) {
+          Range = FPTL.getExceptionSpecRange();
+        }
+      }
+    }
+  }
+  if (!Range.isValid())
+    return;
+
+  StringRef SpecificReplacement = DefaultReplacement;
+  FixItHint FixIt;
+  if (!IsNoThrow)
+    SpecificReplacement = "noexcept(false)";
+  if (IsNoThrow || DefaultReplacement == "noexcept")
+    FixIt = FixItHint::CreateReplacement(CharSourceRange(Range, true),
+                                         SpecificReplacement);
+
+  diag(Range.getBegin(), "found dynamic exception specification '%0'")
+      << makeDynamicExceptionString(*Result.SourceManager,
+                                    CharSourceRange(Range, true))
+      << FixIt;
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/modernize/ModernizeTidyModule.cpp
===================================================================
--- clang-tidy/modernize/ModernizeTidyModule.cpp
+++ clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -23,6 +23,7 @@
 #include "UseAutoCheck.h"
 #include "UseBoolLiteralsCheck.h"
 #include "UseDefaultCheck.h"
+#include "UseNoexceptCheck.h"
 #include "UseNullptrCheck.h"
 #include "UseOverrideCheck.h"
 
@@ -54,6 +55,7 @@
     CheckFactories.registerCheck<UseBoolLiteralsCheck>(
         "modernize-use-bool-literals");
     CheckFactories.registerCheck<UseDefaultCheck>("modernize-use-default");
+    CheckFactories.registerCheck<UseNoexceptCheck>("modernize-use-noexcept");
     CheckFactories.registerCheck<UseNullptrCheck>("modernize-use-nullptr");
     CheckFactories.registerCheck<UseOverrideCheck>("modernize-use-override");
   }
Index: clang-tidy/modernize/CMakeLists.txt
===================================================================
--- clang-tidy/modernize/CMakeLists.txt
+++ clang-tidy/modernize/CMakeLists.txt
@@ -17,6 +17,7 @@
   UseAutoCheck.cpp
   UseBoolLiteralsCheck.cpp
   UseDefaultCheck.cpp
+  UseNoexceptCheck.cpp
   UseNullptrCheck.cpp
   UseOverrideCheck.cpp
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to