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

Addressed most comments.


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
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.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,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' uses dynamic exception specification 'throw()'; use 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: void foo() noexcept;
+
+void bar() throw(...);
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'bar' uses dynamic exception specification 'throw(...)'; use 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void bar() noexcept(false);
+
+void foobar() throw(A, B)
+{}
+// CHECK-MESSAGES: :[[@LINE-2]]:6: warning: function 'foobar' uses dynamic exception specification 'throw(A, B)'; use 'noexcept(false)' instead [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' uses dynamic exception specification 'throw()'; use 'NOEXCEPT' instead [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: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -146,6 +146,11 @@
   Selectively replaces string literals containing escaped characters with raw
   string literals.
 
+- New `modernize-use-noexcept
+  <http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-noexcept.html>`_ check
+
+  Replaces dynamic exception specifications with noexcept.
+
 - New `performance-faster-string-find
   <http://clang.llvm.org/extra/clang-tidy/checks/performance-faster-string-find.html>`_ check
 
Index: clang-tidy/utils/LexerUtils.h
===================================================================
--- clang-tidy/utils/LexerUtils.h
+++ clang-tidy/utils/LexerUtils.h
@@ -12,6 +12,7 @@
 
 #include "clang/AST/ASTContext.h"
 #include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallVector.h"
 
 namespace clang {
 namespace tidy {
@@ -22,6 +23,10 @@
 Token getPreviousNonCommentToken(const ASTContext &Context,
                                  SourceLocation Location);
 
+SmallVector<Token, 16> ParseTokens(const ASTContext &Context,
+                                   const SourceManager &Sources,
+                                   CharSourceRange Range);
+
 } // namespace lexer_utils
 } // namespace tidy
 } // namespace clang
Index: clang-tidy/utils/LexerUtils.cpp
===================================================================
--- clang-tidy/utils/LexerUtils.cpp
+++ clang-tidy/utils/LexerUtils.cpp
@@ -34,6 +34,33 @@
   return Token;
 }
 
+SmallVector<Token, 16> ParseTokens(const ASTContext &Context,
+                                   const SourceManager &Sources,
+                                   CharSourceRange Range) {
+  std::pair<FileID, unsigned> LocInfo =
+      Sources.getDecomposedLoc(Range.getBegin());
+  StringRef File = Sources.getBufferData(LocInfo.first);
+  const char *TokenBegin = File.data() + LocInfo.second;
+  Lexer RawLexer(Sources.getLocForStartOfFile(LocInfo.first),
+                 Context.getLangOpts(), File.begin(), TokenBegin, File.end());
+  SmallVector<Token, 16> Tokens;
+  Token Tok;
+  while (!RawLexer.LexFromRawLexer(Tok)) {
+    if (Tok.is(tok::semi) || Tok.is(tok::l_brace))
+      break;
+    if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation()))
+      break;
+    if (Tok.is(tok::raw_identifier)) {
+      IdentifierInfo &Info = Context.Idents.get(StringRef(
+          Sources.getCharacterData(Tok.getLocation()), Tok.getLength()));
+      Tok.setIdentifierInfo(&Info);
+      Tok.setKind(Info.getTokenID());
+    }
+    Tokens.push_back(Tok);
+  }
+  return Tokens;
+}
+
 } // namespace lexer_utils
 } // namespace tidy
 } // namespace clang
Index: clang-tidy/modernize/UseNoexceptCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/modernize/UseNoexceptCheck.h
@@ -0,0 +1,48 @@
+//===--- 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(true).
+/// \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);
+  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 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,132 @@
+//===--- 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 "../utils/LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+
+// FIXME: Move to ASTMatchers.h on acceptance.
+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(hasDynamicExceptionSpec())
+///   matches the declarations of h, i, and j, but not f, g or k.
+AST_MATCHER(FunctionDecl, hasDynamicExceptionSpec) {
+  if (const auto *FnTy = Node.getType()->getAs<FunctionProtoType>())
+    return FnTy->hasDynamicExceptionSpec();
+  return false;
+}
+}
+
+namespace tidy {
+namespace modernize {
+
+UseNoexceptCheck::UseNoexceptCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      ReplacementStr(Options.get("ReplacementString", "noexcept")) {}
+
+void UseNoexceptCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "ReplacementString", ReplacementStr);
+}
+
+void UseNoexceptCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+  Finder->addMatcher(
+      functionDecl(hasDynamicExceptionSpec()).bind("functionDecl"), this);
+}
+
+void UseNoexceptCheck::check(const 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 CurrentLoc = Range.getEnd();
+  SourceLocation ReplaceStart;
+  SourceLocation ReplaceEnd;
+  std::string Replacement{ReplacementStr};
+  unsigned TokenLength{0};
+
+  SmallVector<Token, 16> Tokens{
+      lexer_utils::ParseTokens(Context, SM, CharSourceRange(Range, true))};
+  auto TokensEnd = Tokens.rend();
+  for (auto it = Tokens.rbegin(); it != TokensEnd; ++it) {
+    SourceLocation Loc = it->getLocation();
+    TokenLength = it->getLength();
+
+    // Looking for throw(), throw(<exception>[,...]), or throw(...).
+    if (it->is(tok::r_paren)) {
+      if (++it == TokensEnd)
+        return;
+      bool empty = true;
+      // Found ')', now loop till we find '('.
+      while (it->isNot(tok::l_paren)) {
+        empty = false;
+        if (++it == TokensEnd)
+          return;
+      }
+      if (++it == TokensEnd)
+        return;
+      if (StringRef(SM.getCharacterData(it->getLocation()), it->getLength()) ==
+          "throw") {
+        if (!empty) {
+          // We only support macro replacement for "throw()".
+          if (Replacement != "noexcept")
+            break;
+          Replacement = "noexcept(false)";
+        }
+        ReplaceEnd = Loc;
+        ReplaceStart = it->getLocation();
+        break;
+      }
+    } else if (++it == TokensEnd)
+      return;
+    CurrentLoc = it->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' uses dynamic exception "
+                                  "specification '%1'; use '%2' instead")
+        << 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