hintonda updated this revision to Diff 52322.
hintonda added a comment.

Address comments and refactor.


http://reviews.llvm.org/D18575

Files:
  clang-tidy/modernize/CMakeLists.txt
  clang-tidy/modernize/ModernizeTidyModule.cpp
  clang-tidy/modernize/UseNoexceptCheck.cpp
  clang-tidy/modernize/UseNoexceptCheck.h
  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,22 @@
+// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \
+// RUN:   -- -std=c++11
+
+class A{};
+class B{};
+
+void foo() throw();
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'foo': Replace dynamic exception specifications 'throw()' with 'noexcept' [modernize-use-noexcept]
+// CHECK-FIXES: void foo() noexcept;
+
+void bar() throw(...);
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'bar': Replace dynamic exception specifications 'throw(...)' with 'noexcept(false)' [modernize-use-noexcept]
+// CHECK-FIXES: void bar() noexcept(false);
+
+void foobar() throw(A, B)
+{}
+// CHECK-MESSAGES: :[[@LINE-2]]:6: warning: function 'foobar': Replace dynamic exception specifications 'throw(A, B)' with 'noexcept(false)' [modernize-use-noexcept]
+// CHECK-FIXES: void foobar() noexcept(false)
+
+// Should not trigger a replacement.
+void titi() noexcept {}
+void toto() noexcept(true) {}
Index: test/clang-tidy/modernize-use-noexcept-macro.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/modernize-use-noexcept-macro.cpp
@@ -0,0 +1,13 @@
+// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \
+// RUN:   -config="{CheckOptions: [{key: modernize-use-noexcept.ReplacementString, value: 'NOEXCEPT'}]}" \
+// RUN:   -- -std=c++11
+
+#define NOEXCEPT noexcept
+
+void bar() throw() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'bar': Replace dynamic exception specifications 'throw()' with 'NOEXCEPT' [modernize-use-noexcept]
+// CHECK-FIXES: void bar() 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,50 @@
+.. 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
+``throw(false)``.  Additinally, users can also use
+:option:``ReplacementString`` to specify a macro to use instead of
+``noexcept``.  This is useful when maintaining source code that must
+compile on older compilers that don't support the ``noexcept``
+keyword.
+
+Example
+^^^^^^^
+
+.. code-block:: c++
+
+  void foo() throw() {}
+
+transforms to:
+
+.. code-block:: c++
+
+  void foo() NOEXCEPT {}
+
+if the ``ReplacementString`` option is set to ``NOEXCEPT``.
Index: clang-tidy/modernize/UseNoexceptCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/modernize/UseNoexceptCheck.h
@@ -0,0 +1,53 @@
+//===--- 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[({true|false})] or a user defined macro.
+/// \code
+///   void foo() throw();
+/// \endcode
+/// Is converted to:
+/// \code
+///   void foo() noexcept;
+/// \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)
+      : ClangTidyCheck(Name, Context),
+        ReplacementStr(Options.get("ReplacementString", "noexcept")) {}
+
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override {
+    Options.store(Opts, "ReplacementString", ReplacementStr);
+  }
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  const std::string ReplacementStr;
+};
+
+} // 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,168 @@
+//===--- 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"
+#include "clang/Lex/Lexer.h"
+
+namespace clang {
+
+// FIXME: Should this be moved to ASTMatchers.h?
+namespace ast_matchers {
+/// \brief Matches functions that have a dynamic exception specification.
+///
+/// Given:
+/// \code
+///   void f();
+///   void g() noexcept;
+///   void h() throw();
+///   void i() throw(int);
+///   void j() throw(...);
+///   void k() noexcept(false);
+/// \endcode
+/// functionDecl(isDynamicException())
+///   matches the declarations of h, i, and j, but not f, g or k.
+AST_MATCHER(FunctionDecl, isDynamicException) {
+  const auto *FnTy = Node.getType()->getAs<FunctionProtoType>();
+  if (!FnTy)
+    return false;
+  return isDynamicExceptionSpec(FnTy->getExceptionSpecType());
+}
+}
+
+namespace tidy {
+namespace modernize {
+
+namespace {
+
+class SimpleReverseLexer {
+public:
+  SimpleReverseLexer(const ASTContext &Context, SourceManager &SM,
+                     SourceLocation StartLoc, SourceLocation EndLoc)
+      : Context(Context), SM(SM), StartLoc(StartLoc),
+        CurrentLoc(EndLoc.getLocWithOffset(1)) {
+    consumeToken();
+  }
+
+  Token &getToken() { return Tok; }
+
+  bool consumeToken() {
+    Tok.setKind(tok::unknown);
+    CurrentLoc = CurrentLoc.getLocWithOffset(-1);
+    BeforeThanCompare<SourceLocation> isBefore{SM};
+    while (isBefore(StartLoc, CurrentLoc)) {
+      CurrentLoc =
+          Lexer::GetBeginningOfToken(CurrentLoc, SM, Context.getLangOpts());
+      if (!Lexer::getRawToken(CurrentLoc, Tok, SM, Context.getLangOpts()) &&
+          !Tok.is(tok::comment)) {
+        break;
+      }
+      CurrentLoc = CurrentLoc.getLocWithOffset(-1);
+    }
+    return !Tok.is(tok::unknown);
+  }
+
+private:
+  const ASTContext &Context;
+  SourceManager &SM;
+  SourceLocation StartLoc;
+  SourceLocation CurrentLoc;
+  Token Tok;
+};
+} // anonymous namespace
+
+void UseNoexceptCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+  Finder->addMatcher(
+      ast_matchers::functionDecl(ast_matchers::isDynamicException())
+          .bind("functionDecl"),
+      this);
+}
+
+void UseNoexceptCheck::check(
+    const ast_matchers::MatchFinder::MatchResult &Result) {
+  SourceManager &SM = *Result.SourceManager;
+  const ASTContext &Context = *Result.Context;
+
+  const FunctionDecl *FuncDecl =
+      Result.Nodes.getNodeAs<clang::FunctionDecl>("functionDecl");
+  if (!FuncDecl)
+    return;
+
+  SourceRange Range = FuncDecl->getSourceRange();
+  if (!Range.isValid())
+    return;
+
+  SourceLocation BeginLoc = Range.getBegin();
+  SourceLocation CurrentLoc = Range.getEnd();
+
+  SourceLocation ReplaceStart;
+  SourceLocation ReplaceEnd;
+
+  SimpleReverseLexer Lexer{Context, SM, BeginLoc, CurrentLoc};
+  Token &Tok = Lexer.getToken();
+  unsigned TokenLength{0};
+
+  std::string Replacement{ReplacementStr};
+  BeforeThanCompare<SourceLocation> isBefore(SM);
+  while (isBefore(BeginLoc, CurrentLoc)) {
+    SourceLocation Loc = Tok.getLocation();
+    TokenLength = Tok.getLength();
+
+    // Looking for throw(), throw(<exception>[,...]), or throw(...).
+    if (Tok.is(tok::r_paren)) {
+      if (!Lexer.consumeToken())
+        return;
+      // if (!isBefore(BeginLoc, Tok.getLocation())) return;
+      bool empty = true;
+      // Found ')', now loop till we find '('.
+      while (Tok.isNot(tok::l_paren)) {
+        empty = false;
+        if (!Lexer.consumeToken())
+          return;
+      }
+      if (!Lexer.consumeToken())
+        return;
+      if (StringRef(SM.getCharacterData(Tok.getLocation()), Tok.getLength()) ==
+          "throw") {
+        if (!empty) {
+          // We only support macro replacement for "throw()"
+          if (Replacement != "noexcept")
+            break;
+          Replacement = "noexcept(false)";
+        }
+        ReplaceEnd = Loc;
+        ReplaceStart = Tok.getLocation();
+        break;
+      }
+    } else if (!Lexer.consumeToken())
+      return;
+    CurrentLoc = Tok.getLocation();
+  }
+
+  if (ReplaceStart.isValid() && ReplaceEnd.isValid()) {
+    std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(ReplaceStart);
+    std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(ReplaceEnd);
+    if (beginInfo.first != endInfo.first || beginInfo.second > endInfo.second)
+      return;
+    auto Len = endInfo.second - beginInfo.second + TokenLength;
+    diag(FuncDecl->getLocation(), "function '%0': Replace dynamic exception "
+                                  "specifications '%1' with '%2'")
+        << FuncDecl->getNameInfo().getAsString()
+        << StringRef(SM.getCharacterData(ReplaceStart), Len) << Replacement
+        << FixItHint::CreateReplacement(
+               CharSourceRange::getTokenRange(ReplaceStart, ReplaceEnd),
+               Replacement);
+  }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/modernize/ModernizeTidyModule.cpp
===================================================================
--- clang-tidy/modernize/ModernizeTidyModule.cpp
+++ clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -20,6 +20,7 @@
 #include "ShrinkToFitCheck.h"
 #include "UseAutoCheck.h"
 #include "UseDefaultCheck.h"
+#include "UseNoexceptCheck.h"
 #include "UseNullptrCheck.h"
 #include "UseOverrideCheck.h"
 
@@ -46,6 +47,7 @@
     CheckFactories.registerCheck<ShrinkToFitCheck>("modernize-shrink-to-fit");
     CheckFactories.registerCheck<UseAutoCheck>("modernize-use-auto");
     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
@@ -13,6 +13,7 @@
   ShrinkToFitCheck.cpp
   UseAutoCheck.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