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 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("modernize-shrink-to-fit"); CheckFactories.registerCheck("modernize-use-auto"); CheckFactories.registerCheck("modernize-use-default"); + CheckFactories.registerCheck("modernize-use-noexcept"); CheckFactories.registerCheck("modernize-use-nullptr"); CheckFactories.registerCheck("modernize-use-override"); } 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; + +/// \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(); + 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 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("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 isBefore(SM); + while (isBefore(BeginLoc, CurrentLoc)) { + SourceLocation Loc = Tok.getLocation(); + TokenLength = Tok.getLength(); + + // Looking for throw(), throw([,...]), 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 beginInfo = SM.getDecomposedLoc(ReplaceStart); + std::pair 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: 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([,...]), 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([,...])`` 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: 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: 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) {}