hintonda created this revision. hintonda added reviewers: alexfh, aaron.ballman. hintonda added subscribers: etienneb, Eugene.Zelenko, cfe-commits.
[clang-tidy] New checker to replace dynamic exception specifications This is an alternative to D18575 which relied on reparsing the decl to find the location of dynamic exception specifications, but couldn't deal with preprocessor conditionals correctly without reparsing the entire file. This approach uses D20428 to find dynamic exception specifiaction locations and handles all cases correctly. http://reviews.llvm.org/D20693 Files: clang-tidy/modernize/CMakeLists.txt clang-tidy/modernize/ModernizeTidyModule.cpp clang-tidy/modernize/UseNoexceptCheck.cpp clang-tidy/modernize/UseNoexceptCheck.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,56 @@ +// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \ +// RUN: -- -std=c++11 + +class A {}; +class B {}; + +void foo() throw(); +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: found dynamic exception specification 'throw()' [modernize-use-noexcept] +// CHECK-FIXES: void foo() noexcept; + +void bar() throw(...); +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: found dynamic exception specification 'throw(...)' [modernize-use-noexcept] +// CHECK-FIXES: void bar() noexcept(false); + +void k() throw(int(int)); +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: found dynamic exception specification 'throw(int(int))' [modernize-use-noexcept] +// CHECK-FIXES: void k() noexcept(false); + +void foobar() throw(A, B) +{} +// CHECK-MESSAGES: :[[@LINE-2]]:15: warning: found dynamic exception specification 'throw(A, B)' [modernize-use-noexcept] +// CHECK-FIXES: void foobar() noexcept(false) + +void baz(int = (throw A(), 0)) throw(A, B) {} +// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: found dynamic exception specification 'throw(A, B)' [modernize-use-noexcept] +// CHECK-FIXES: void baz(int = (throw A(), 0)) noexcept(false) {} + +void g(void (*fp)(void) throw()); +// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: found dynamic exception specification 'throw()' [modernize-use-noexcept] +// CHECK-FIXES: void g(void (*fp)(void) noexcept); + +void f(void (*fp)(void) throw()) throw(char); +// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: found dynamic exception specification 'throw()' [modernize-use-noexcept] +// CHECK-MESSAGES: :[[@LINE-2]]:34: warning: found dynamic exception specification 'throw(char)' [modernize-use-noexcept] +// CHECK-FIXES: void f(void (*fp)(void) noexcept) noexcept(false); + +void j() throw(int(int) throw(void(void) throw(int))); +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: found dynamic exception specification 'throw(int(int) throw(void(void) throw(int)))' [modernize-use-noexcept] +// CHECK-FIXES: void j() noexcept(false); + +class Y { + Y() throw() = default; +}; +// CHECK-MESSAGES: :[[@LINE-2]]:7: warning: found dynamic exception specification 'throw()' [modernize-use-noexcept] +// CHECK-FIXES: Y() noexcept = default; + +// Should not trigger a replacement. +void titi() noexcept {} +void toto() noexcept(true) {} + +// Should not trigger a replacement. +void bad() +#if !__has_feature(cxx_noexcept) + throw() +#endif + ; Index: test/clang-tidy/modernize-use-noexcept-macro.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-use-noexcept-macro.cpp @@ -0,0 +1,24 @@ +// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \ +// RUN: -config="{CheckOptions: [{key: modernize-use-noexcept.ReplacementString, value: 'NOEXCEPT'}]}" \ +// RUN: -- -std=c++11 + +// Example definition of NOEXCEPT -- simplified test to see if noexcept is supported. +#if (__has_feature(cxx_noexcept)) +#define NOEXCEPT noexcept +#else +#define NOEXCEPT throw() +#endif + +void bar() throw() {} +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: found dynamic exception specification 'throw()' [modernize-use-noexcept] +// CHECK-FIXES: void bar() NOEXCEPT {} + +// Should not trigger a FixItHint, since macros only support noexcept, and this +// case throws. +class A {}; +class B {}; +void foobar() throw(A, B); +// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: found dynamic exception specification 'throw(A, B)' [modernize-use-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,55 @@ +.. 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 +``noexcept(false)``. Additionally, users can also use +:option:`ReplacementString` to specify a macro to use instead of +``noexcept``. This is useful when maintaining source code that must +be compiled with older compilers that don't support the ``noexcept`` +keyword. Users can define the macro to be ``noexcept`` or ``throw()`` +depending on whether or not noexcept is supported. + +Please note that since ``throw(int)`` is equivelent to +``noexcept(false)`` not ``noexcept``, this check will detect, but not +provide a FixItHint in that case. + +Example +^^^^^^^ + +.. code-block:: c++ + + void foo() throw() {} + +transforms to: + +.. code-block:: c++ + + void foo() NOEXCEPT {} + +if the :option:`ReplacementString` option is set to `NOEXCEPT`. Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -206,6 +206,11 @@ Finds integer literals which are cast to ``bool``. +- New `modernize-use-noexcept + <http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-noexcept.html>`_ check + + Replaces dynamic exception specifications with ``noexcept`` or a user defined macro. + - New `performance-faster-string-find <http://clang.llvm.org/extra/clang-tidy/checks/performance-faster-string-find.html>`_ check Index: clang-tidy/modernize/UseNoexceptCheck.h =================================================================== --- /dev/null +++ clang-tidy/modernize/UseNoexceptCheck.h @@ -0,0 +1,50 @@ +//===--- 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(false)`. +/// \code +/// void foo() throw(); +/// void bar() throw(int); +/// \endcode +/// Is converted to: +/// \code +/// void foo() noexcept; +// void bar() noexcept(false); +/// \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 DefaultReplacement; +}; + +} // 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,102 @@ +//===--- 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" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { + +static StringRef +makeDynamicExceptionString(const SourceManager &SM, + const CharSourceRange &FileMoveRange) { + std::pair<FileID, unsigned> BeginInfo = + SM.getDecomposedLoc(FileMoveRange.getBegin()); + std::pair<FileID, unsigned> EndInfo = + SM.getDecomposedLoc(FileMoveRange.getEnd()); + + // Add 1 which represents the size of the trailing ')'. + auto Len = EndInfo.second - BeginInfo.second + 1; + return StringRef(SM.getCharacterData(FileMoveRange.getBegin()), Len); +} + +UseNoexceptCheck::UseNoexceptCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + DefaultReplacement(Options.get("ReplacementString", "noexcept")) {} + +void UseNoexceptCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "ReplacementString", DefaultReplacement); +} + +void UseNoexceptCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + + Finder->addMatcher( + functionDecl(hasTypeLoc(loc(functionType(hasDynamicExceptionSpec())))) + .bind("funcDecl"), + this); + Finder->addMatcher( + parmVarDecl(hasType(pointerType(pointee(parenType( + innerType(functionType(hasDynamicExceptionSpec()))))))) + .bind("parmVarDecl"), + this); +} + +void UseNoexceptCheck::check(const MatchFinder::MatchResult &Result) { + bool IsNoThrow = false; + SourceRange Range; + + if (const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl")) { + if (auto const *FnTy = FuncDecl->getType()->getAs<FunctionProtoType>()) + IsNoThrow = FnTy->isNothrow(*Result.Context); + if (const auto *Info = FuncDecl->getTypeSourceInfo()) + Range = + Info->getTypeLoc().castAs<FunctionTypeLoc>().getExceptionSpecRange(); + } + if (const auto *ParmDecl = + Result.Nodes.getNodeAs<ParmVarDecl>("parmVarDecl")) { + if (const auto *PtrTy = ParmDecl->getType()->getAs<PointerType>()) + if (const auto *FnTy = + PtrTy->getPointeeType()->getAs<FunctionProtoType>()) + IsNoThrow = FnTy->isNothrow(*Result.Context); + + if (const TypeSourceInfo *TSI = ParmDecl->getTypeSourceInfo()) { + TypeLoc TL = TSI->getTypeLoc(); + if (TL.getType()->isPointerType()) { + TL = TL.getNextTypeLoc().IgnoreParens(); + if (auto FPTL = TL.getAs<FunctionProtoTypeLoc>()) { + Range = FPTL.getExceptionSpecRange(); + } + } + } + } + if (!Range.isValid()) + return; + + StringRef SpecificReplacement = DefaultReplacement; + FixItHint FixIt; + if (!IsNoThrow) + SpecificReplacement = "noexcept(false)"; + if (IsNoThrow || DefaultReplacement == "noexcept") + FixIt = FixItHint::CreateReplacement(CharSourceRange(Range, true), + SpecificReplacement); + + diag(Range.getBegin(), "found dynamic exception specification '%0'") + << makeDynamicExceptionString(*Result.SourceManager, + CharSourceRange(Range, true)) + << FixIt; +} + +} // namespace modernize +} // namespace tidy +} // namespace clang Index: clang-tidy/modernize/ModernizeTidyModule.cpp =================================================================== --- clang-tidy/modernize/ModernizeTidyModule.cpp +++ clang-tidy/modernize/ModernizeTidyModule.cpp @@ -23,6 +23,7 @@ #include "UseAutoCheck.h" #include "UseBoolLiteralsCheck.h" #include "UseDefaultCheck.h" +#include "UseNoexceptCheck.h" #include "UseNullptrCheck.h" #include "UseOverrideCheck.h" @@ -54,6 +55,7 @@ CheckFactories.registerCheck<UseBoolLiteralsCheck>( "modernize-use-bool-literals"); 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 @@ -17,6 +17,7 @@ UseAutoCheck.cpp UseBoolLiteralsCheck.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