[clang-tidy] Replace the usage of std::uncaught_exception with std::uncaught_exceptions

Patch by: Daniel Kolozsvari!

Differential Revision: https://reviews.llvm.org/D40787


git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@325572 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/clang-tidy/modernize/CMakeLists.txt b/clang-tidy/modernize/CMakeLists.txt
index e344ada..afa1140 100644
--- a/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tidy/modernize/CMakeLists.txt
@@ -27,6 +27,7 @@
   UseNullptrCheck.cpp
   UseOverrideCheck.cpp
   UseTransparentFunctorsCheck.cpp
+  UseUncaughtExceptionsCheck.cpp
   UseUsingCheck.cpp
 
   LINK_LIBS
diff --git a/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tidy/modernize/ModernizeTidyModule.cpp
index a2f05d6..63b7d02 100644
--- a/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -33,6 +33,7 @@
 #include "UseNullptrCheck.h"
 #include "UseOverrideCheck.h"
 #include "UseTransparentFunctorsCheck.h"
+#include "UseUncaughtExceptionsCheck.h"
 #include "UseUsingCheck.h"
 
 using namespace clang::ast_matchers;
@@ -78,6 +79,8 @@
     CheckFactories.registerCheck<UseOverrideCheck>("modernize-use-override");
     CheckFactories.registerCheck<UseTransparentFunctorsCheck>(
         "modernize-use-transparent-functors");
+    CheckFactories.registerCheck<UseUncaughtExceptionsCheck>(
+        "modernize-use-uncaught-exceptions");
     CheckFactories.registerCheck<UseUsingCheck>("modernize-use-using");
   }
 
diff --git a/clang-tidy/modernize/UseUncaughtExceptionsCheck.cpp b/clang-tidy/modernize/UseUncaughtExceptionsCheck.cpp
new file mode 100644
index 0000000..9ac6e1d
--- /dev/null
+++ b/clang-tidy/modernize/UseUncaughtExceptionsCheck.cpp
@@ -0,0 +1,104 @@
+//===--- UseUncaughtExceptionsCheck.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 "UseUncaughtExceptionsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+void UseUncaughtExceptionsCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus17)
+    return;
+
+  std::string MatchText = "::std::uncaught_exception";
+
+  // Using declaration: warning and fix-it.
+  Finder->addMatcher(
+      usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(hasName(MatchText))))
+          .bind("using_decl"),
+      this);
+
+  // DeclRefExpr: warning, no fix-it.
+  Finder->addMatcher(declRefExpr(allOf(to(functionDecl(hasName(MatchText))),
+                                       unless(callExpr())))
+                         .bind("decl_ref_expr"),
+                     this);
+
+  // CallExpr: warning, fix-it.
+  Finder->addMatcher(
+      callExpr(allOf(hasDeclaration(functionDecl(hasName(MatchText))),
+                     unless(hasAncestor(initListExpr()))))
+          .bind("call_expr"),
+      this);
+  // CallExpr in initialisation list: warning, fix-it with avoiding narrowing
+  // conversions.
+  Finder->addMatcher(
+      callExpr(allOf(hasAncestor(initListExpr()),
+                     hasDeclaration(functionDecl(hasName(MatchText)))))
+          .bind("init_call_expr"),
+      this);
+}
+
+void UseUncaughtExceptionsCheck::check(const MatchFinder::MatchResult &Result) {
+  SourceLocation BeginLoc;
+  SourceLocation EndLoc;
+  const CallExpr *C = Result.Nodes.getNodeAs<CallExpr>("init_call_expr");
+  bool WarnOnly = false;
+
+  if (C) {
+    BeginLoc = C->getLocStart();
+    EndLoc = C->getLocEnd();
+  } else if (const auto *E = Result.Nodes.getNodeAs<CallExpr>("call_expr")) {
+    BeginLoc = E->getLocStart();
+    EndLoc = E->getLocEnd();
+  } else if (const auto *D =
+                 Result.Nodes.getNodeAs<DeclRefExpr>("decl_ref_expr")) {
+    BeginLoc = D->getLocStart();
+    EndLoc = D->getLocEnd();
+    WarnOnly = true;
+  } else {
+    const auto *U = Result.Nodes.getNodeAs<UsingDecl>("using_decl");
+    assert(U && "Null pointer, no node provided");
+    BeginLoc = U->getNameInfo().getBeginLoc();
+    EndLoc = U->getNameInfo().getEndLoc();
+  }
+
+  auto Diag = diag(BeginLoc, "'std::uncaught_exception' is deprecated, use "
+                             "'std::uncaught_exceptions' instead");
+
+  if (!BeginLoc.isMacroID()) {
+    StringRef Text =
+        Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
+                             *Result.SourceManager, getLangOpts());
+
+    Text.consume_back("()");
+    int TextLength = Text.size();
+
+    if (WarnOnly) {
+      return;
+    }
+
+    if (!C) {
+      Diag << FixItHint::CreateInsertion(BeginLoc.getLocWithOffset(TextLength),
+                                         "s");
+    } else {
+      Diag << FixItHint::CreateReplacement(C->getSourceRange(),
+                                           "std::uncaught_exceptions() > 0");
+    }
+  }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/UseUncaughtExceptionsCheck.h b/clang-tidy/modernize/UseUncaughtExceptionsCheck.h
new file mode 100644
index 0000000..2b9660c
--- /dev/null
+++ b/clang-tidy/modernize/UseUncaughtExceptionsCheck.h
@@ -0,0 +1,37 @@
+//===--- UseUncaughtExceptionsCheck.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_UNCAUGHT_EXCEPTIONS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_UNCAUGHT_EXCEPTIONS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// This check will warn on calls to std::uncaught_exception and replace them with calls to
+/// std::uncaught_exceptions, since std::uncaught_exception was deprecated in C++17. In case of
+/// macro ID there will be only a warning without fixits.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-uncaught-exceptions.html
+class UseUncaughtExceptionsCheck : public ClangTidyCheck {
+public:
+  UseUncaughtExceptionsCheck(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_UNCAUGHT_EXCEPTIONS_H
diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst
index 1868cc6..e6d3509 100644
--- a/docs/ReleaseNotes.rst
+++ b/docs/ReleaseNotes.rst
@@ -105,6 +105,11 @@
 
   Diagnoses when a temporary object that appears to be an exception is constructed but not thrown.
 
+- New `modernize-use-uncaught-exceptions
+  <http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-uncaught-exceptions.html>`_ check
+
+  Finds and replaces deprecated uses of std::uncaught_exception to std::uncaught_exceptions()
+
 Improvements to include-fixer
 -----------------------------
 
diff --git a/docs/clang-tidy/checks/list.rst b/docs/clang-tidy/checks/list.rst
index a2c84f1..f50fd71 100644
--- a/docs/clang-tidy/checks/list.rst
+++ b/docs/clang-tidy/checks/list.rst
@@ -177,6 +177,7 @@
    modernize-use-nullptr
    modernize-use-override
    modernize-use-transparent-functors
+   modernize-use-uncaught-exceptions
    modernize-use-using
    mpi-buffer-deref
    mpi-type-mismatch
diff --git a/docs/clang-tidy/checks/modernize-use-uncaught-exceptions.rst b/docs/clang-tidy/checks/modernize-use-uncaught-exceptions.rst
new file mode 100644
index 0000000..ca2a09e
--- /dev/null
+++ b/docs/clang-tidy/checks/modernize-use-uncaught-exceptions.rst
@@ -0,0 +1,64 @@
+.. title:: clang-tidy - modernize-use-uncaught-exceptions
+
+modernize-use-uncaught-exceptions
+====================================
+
+This check will warn on calls to ``std::uncaught_exception`` and replace them with
+calls to ``std::uncaught_exceptions``, since ``std::uncaught_exception`` was deprecated
+in C++17.
+
+Below are a few examples of what kind of occurrences will be found and what
+they will be replaced with.
+
+.. code-block:: c++
+
+	#define MACRO1 std::uncaught_exception
+	#define MACRO2 std::uncaught_exception
+
+	int uncaught_exception() {
+		return 0;
+	}
+
+	int main() {
+		int res;
+
+	  res = uncaught_exception();
+	  // No warning, since it is not the deprecated function from namespace std
+	  
+	  res = MACRO2();
+	  // Warning, but will not be replaced
+	  
+	  res = std::uncaught_exception();
+	  // Warning and replaced
+	  
+	  using std::uncaught_exception;
+	  // Warning and replaced
+	  
+	  res = uncaught_exception();
+	  // Warning and replaced
+	}
+
+After applying the fixes the code will look like the following:
+
+.. code-block:: c++
+
+	#define MACRO1 std::uncaught_exception
+	#define MACRO2 std::uncaught_exception
+
+	int uncaught_exception() {
+		return 0;
+	}
+
+	int main() {
+	  int res;
+	  
+	  res = uncaught_exception();
+	  
+	  res = MACRO2();
+	  
+	  res = std::uncaught_exceptions();
+	  
+	  using std::uncaught_exceptions;
+	  
+	  res = uncaught_exceptions();
+	}
diff --git a/test/clang-tidy/modernize-use-uncaught-exceptions.cpp b/test/clang-tidy/modernize-use-uncaught-exceptions.cpp
new file mode 100644
index 0000000..e6bae2b
--- /dev/null
+++ b/test/clang-tidy/modernize-use-uncaught-exceptions.cpp
@@ -0,0 +1,79 @@
+// RUN: %check_clang_tidy %s modernize-use-uncaught-exceptions %t -- -- -std=c++1z
+#define MACRO std::uncaught_exception
+// CHECK-FIXES: #define MACRO std::uncaught_exception
+
+bool uncaught_exception() {
+  return 0;
+}
+
+namespace std {
+  bool uncaught_exception() {
+    return false;
+  }
+
+  int uncaught_exceptions() {
+    return 0;
+  }
+}
+
+template <typename T>
+bool doSomething(T t) { 
+  return t();
+  // CHECK-FIXES: return t();
+}
+
+template <bool (*T)()>
+bool doSomething2() { 
+  return T();
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'std::uncaught_exception' is deprecated, use 'std::uncaught_exceptions' instead
+  // CHECK-FIXES: return T();
+}
+
+void no_warn() {
+
+  uncaught_exception();
+  // CHECK-FIXES: uncaught_exception();
+
+  doSomething(uncaught_exception);
+  // CHECK-FIXES: doSomething(uncaught_exception);
+}
+
+void warn() {
+
+  std::uncaught_exception();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'std::uncaught_exception' is deprecated, use 'std::uncaught_exceptions' instead
+  // CHECK-FIXES: std::uncaught_exceptions();
+
+  using std::uncaught_exception;
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: 'std::uncaught_exception' is deprecated, use 'std::uncaught_exceptions' instead
+  // CHECK-FIXES: using std::uncaught_exceptions;
+
+  uncaught_exception();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'std::uncaught_exception' is deprecated, use 'std::uncaught_exceptions' instead
+  // CHECK-FIXES: uncaught_exceptions();
+
+  bool b{uncaught_exception()};
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'std::uncaught_exception' is deprecated, use 'std::uncaught_exceptions' instead
+  // CHECK-FIXES: bool b{std::uncaught_exceptions() > 0};
+
+  MACRO();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'std::uncaught_exception' is deprecated, use 'std::uncaught_exceptions' instead
+  // CHECK-FIXES: MACRO();
+
+  doSomething(std::uncaught_exception);
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: 'std::uncaught_exception' is deprecated, use 'std::uncaught_exceptions' instead
+  // CHECK-FIXES: doSomething(std::uncaught_exception);
+
+  doSomething(uncaught_exception);
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: 'std::uncaught_exception' is deprecated, use 'std::uncaught_exceptions' instead
+  // CHECK-FIXES: doSomething(uncaught_exception);
+
+  bool (*foo)();
+  foo = &uncaught_exception;
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'std::uncaught_exception' is deprecated, use 'std::uncaught_exceptions' instead
+  // CHECK-FIXES: foo = &uncaught_exception;
+
+  doSomething2<uncaught_exception>();
+  // CHECK-MESSAGES: [[@LINE-1]]:16: warning: 'std::uncaught_exception' is deprecated, use 'std::uncaught_exceptions' instead
+  // CHECK-FIXES: doSomething2<uncaught_exception>();
+}