Index: clang-tidy/modernize/CMakeLists.txt =================================================================== --- clang-tidy/modernize/CMakeLists.txt +++ clang-tidy/modernize/CMakeLists.txt @@ -12,6 +12,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 @@ -19,6 +19,7 @@ #include "ShrinkToFitCheck.h" #include "UseAutoCheck.h" #include "UseDefaultCheck.h" +#include "UseNoexceptCheck.h" #include "UseNullptrCheck.h" #include "UseOverrideCheck.h" @@ -43,6 +44,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,49 @@ +//===--- 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 noexcept specifications, or macros, with noexcept 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); + 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; + std::string RawCandidateStrings; + CandidateSet Candidates; +}; + +} // 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,133 @@ +//===--- 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" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { + +namespace { +void parseCandidateStrings(StringRef AllStrings, CandidateSet &ResultSet, + char delimiter) { + SmallVector Candidates; + AllStrings.split(Candidates, ','); + for (StringRef &RawString : Candidates) { + StringRef Candidate = RawString.trim(); + if (!Candidate.empty()) + ResultSet.insert(Candidate); + } +} + +using namespace lexer_utils; + +static StringRef GetText(const Token &Tok, const SourceManager &SM) { + return StringRef(SM.getCharacterData(Tok.getLocation()), Tok.getLength()); +} +} // namespace + +UseNoexceptCheck::UseNoexceptCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + ReplacementStr(Options.get("ReplacementString", "noexcept")), + RawCandidateStrings( + Options.get("CandidateStrings", "")) { + // Always append the defaults, even if the user provides their own. + RawCandidateStrings.append(",throw(),noexcept(true)"); + parseCandidateStrings(RawCandidateStrings, Candidates, ','); +} + +void UseNoexceptCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "ReplacementString", ReplacementStr); + Options.store(Opts, "CandidateStrings", RawCandidateStrings); +} + +void UseNoexceptCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + Finder->addMatcher(functionDecl(isNoThrow()).bind("functionDecl"), this); +} + +void UseNoexceptCheck::check(const MatchFinder::MatchResult &Result) { + SourceManager &SM = *Result.SourceManager; + const ASTContext &Context = *Result.Context; + + if (const FunctionDecl *FuncDecl = + Result.Nodes.getNodeAs("functionDecl")) { + + SourceRange Range = FuncDecl->getSourceRange(); + if (!Range.isValid()) + return; + + SourceLocation BeginLoc = Range.getBegin(); + SourceLocation CurrentLoc = Range.getEnd(); + + SourceLocation ReplaceStart; + SourceLocation ReplaceEnd; + + Token Tok = + getPreviousNonCommentToken(Context, CurrentLoc.getLocWithOffset(1)); + + std::string found; + BeforeThanCompare isBefore(SM); + while (isBefore(BeginLoc, CurrentLoc)) { + SourceLocation Loc = Tok.getLocation(); + + if (Tok.is(tok::r_paren)) { + Tok = getPreviousNonCommentToken(Context, Tok.getLocation()); + if (Tok.is(tok::l_paren)) { + Tok = getPreviousNonCommentToken(Context, Tok.getLocation()); + found = std::string(GetText(Tok, SM)) + "()"; + if (Candidates.find(found) != Candidates.end()) { + ReplaceEnd = Loc; + ReplaceStart = Tok.getLocation(); + break; + } + } else if (GetText(Tok, SM) == "true") { + Tok = getPreviousNonCommentToken(Context, Tok.getLocation()); + if (Tok.is(tok::l_paren)) { + Tok = getPreviousNonCommentToken(Context, Tok.getLocation()); + found = std::string(GetText(Tok, SM)) + "(true)"; + if (Candidates.find(found) != Candidates.end()) { + ReplaceEnd = Loc; + ReplaceStart = Tok.getLocation(); + break; + } + } + } + } else { + found = GetText(Tok, SM); + if (Candidates.find(found) != Candidates.end()) { + ReplaceEnd = Loc; + ReplaceStart = Tok.getLocation(); + break; + } + Tok = getPreviousNonCommentToken(Context, Tok.getLocation()); + } + CurrentLoc = Tok.getLocation(); + } + + if (ReplaceStart.isValid() && ReplaceEnd.isValid()) { + diag(FuncDecl->getLocation(), + "function '%0': replacing noexcept specification '%1' with '%2'") + << FuncDecl->getNameInfo().getAsString() << found << ReplacementStr + << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(ReplaceStart, ReplaceEnd), + ReplacementStr); + } + } +} + +} // namespace modernize +} // namespace tidy +} // namespace clang Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -83,6 +83,7 @@ modernize-shrink-to-fit modernize-use-auto modernize-use-default + modernize-use-noexcept modernize-use-nullptr modernize-use-override performance-faster-string-find Index: docs/clang-tidy/checks/modernize-use-noexcept.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/modernize-use-noexcept.rst @@ -0,0 +1,51 @@ +.. title:: clang-tidy - modernize-use-noexcept + +modernize-use-noexcept +====================== + +The check converts exception specifications, e.g., throw(), to either +noexcept, or a user defined macro. + +Example +------- + +.. code-block:: c++ + + void foo() throw(); + +transforms to: + +.. code-block:: c++ + + void foo() noexcept; + + +User defined macros +------------------- + +By default this check will only replace the ``throw()`` and +``noexcept(true)`` with ``noexcept``. However, the user can use +:option:``CandidateStrings" to specify a comma-separated list of macro +names that will be transformed along with ``throw()`` and +``noexcept(true)``. + +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() NOTHROW {} + +transforms to: + +.. code-block:: c++ + + void foo() NOEXCEPT {} + +if the ``CandidateStrings`` option is set to ``NOTHROW``, and 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,19 @@ +// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \ +// RUN: -config="{CheckOptions: [{key: modernize-use-noexcept.CandidateStrings, value: ' NOTHROW , , ,'}, \ +// RUN: {key: modernize-use-noexcept.ReplacementString, value: 'NOEXCEPT'}]}" \ +// RUN: -- -std=c++11 + +#define NOEXCEPT noexcept + +void foo() noexcept(true); +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'foo': replacing noexcept specification 'noexcept(true)' with 'NOEXCEPT' [modernize-use-noexcept] +// CHECK-FIXES: void foo() NOEXCEPT; + +void bar() throw() {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'bar': replacing noexcept specification 'throw()' with 'NOEXCEPT' [modernize-use-noexcept] +// CHECK-FIXES: void bar() NOEXCEPT {} + +#define NOTHROW throw() +void foobar() NOTHROW {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'foobar': replacing noexcept specification 'NOTHROW' with 'NOEXCEPT' [modernize-use-noexcept] +// CHECK-FIXES: void foobar() NOEXCEPT {} Index: test/clang-tidy/modernize-use-noexcept.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-use-noexcept.cpp @@ -0,0 +1,16 @@ +// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \ +// RUN: -config="{CheckOptions: [{key: modernize-use-noexcept.CandidateStrings, value: 'NOTHROW,,,'}]}" \ +// RUN: -- -std=c++11 + +void foo() throw(); +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'foo': replacing noexcept specification 'throw()' with 'noexcept' [modernize-use-noexcept] +// CHECK-FIXES: void foo() noexcept; + +void bar() throw() {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'bar': replacing noexcept specification 'throw()' with 'noexcept' [modernize-use-noexcept] +// CHECK-FIXES: void bar() noexcept {} + +#define NOTHROW throw() +void foobar() NOTHROW {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'foobar': replacing noexcept specification 'NOTHROW' with 'noexcept' [modernize-use-noexcept] +// CHECK-FIXES: void foobar() noexcept {}