Skip to content

Commit

Permalink
[clang-tidy] New checker to replace dynamic exception specifications
Browse files Browse the repository at this point in the history
Summary:
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 specification
locations and handles all cases correctly.

Reviewers: aaron.ballman, alexfh

Reviewed By: aaron.ballman, alexfh

Subscribers: xazax.hun, mgehre, malcolm.parsons, mgorny, JDevlieghere, cfe-commits, Eugene.Zelenko, etienneb

Patch by Don Hinton!

Differential Revision: https://reviews.llvm.org/D20693

llvm-svn: 304977
  • Loading branch information
alexfh committed Jun 8, 2017
1 parent c3c7212 commit 08936e4
Show file tree
Hide file tree
Showing 10 changed files with 490 additions and 0 deletions.
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
Expand Up @@ -22,6 +22,7 @@ add_clang_library(clangTidyModernizeModule
UseEmplaceCheck.cpp
UseEqualsDefaultCheck.cpp
UseEqualsDeleteCheck.cpp
UseNoexceptCheck.cpp
UseNullptrCheck.cpp
UseOverrideCheck.cpp
UseTransparentFunctorsCheck.cpp
Expand Down
Expand Up @@ -28,6 +28,7 @@
#include "UseEmplaceCheck.h"
#include "UseEqualsDefaultCheck.h"
#include "UseEqualsDeleteCheck.h"
#include "UseNoexceptCheck.h"
#include "UseNullptrCheck.h"
#include "UseOverrideCheck.h"
#include "UseTransparentFunctorsCheck.h"
Expand Down Expand Up @@ -69,6 +70,7 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<UseEqualsDefaultCheck>("modernize-use-equals-default");
CheckFactories.registerCheck<UseEqualsDeleteCheck>(
"modernize-use-equals-delete");
CheckFactories.registerCheck<UseNoexceptCheck>("modernize-use-noexcept");
CheckFactories.registerCheck<UseNullptrCheck>("modernize-use-nullptr");
CheckFactories.registerCheck<UseOverrideCheck>("modernize-use-override");
CheckFactories.registerCheck<UseTransparentFunctorsCheck>(
Expand Down
114 changes: 114 additions & 0 deletions clang-tools-extra/clang-tidy/modernize/UseNoexceptCheck.cpp
@@ -0,0 +1,114 @@
//===--- 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"

using namespace clang::ast_matchers;

namespace clang {
namespace tidy {
namespace modernize {

UseNoexceptCheck::UseNoexceptCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
NoexceptMacro(Options.get("ReplacementString", "")),
UseNoexceptFalse(Options.get("UseNoexceptFalse", true)) {}

void UseNoexceptCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "ReplacementString", NoexceptMacro);
Options.store(Opts, "UseNoexceptFalse", UseNoexceptFalse);
}

void UseNoexceptCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus11)
return;

Finder->addMatcher(
functionDecl(
cxxMethodDecl(
hasTypeLoc(loc(functionProtoType(hasDynamicExceptionSpec()))),
anyOf(hasOverloadedOperatorName("delete[]"),
hasOverloadedOperatorName("delete"), cxxDestructorDecl()))
.bind("del-dtor"))
.bind("funcDecl"),
this);

Finder->addMatcher(
functionDecl(
hasTypeLoc(loc(functionProtoType(hasDynamicExceptionSpec()))),
unless(anyOf(hasOverloadedOperatorName("delete[]"),
hasOverloadedOperatorName("delete"),
cxxDestructorDecl())))
.bind("funcDecl"),
this);

Finder->addMatcher(
parmVarDecl(anyOf(hasType(pointerType(pointee(parenType(innerType(
functionProtoType(hasDynamicExceptionSpec())))))),
hasType(memberPointerType(pointee(parenType(innerType(
functionProtoType(hasDynamicExceptionSpec()))))))))
.bind("parmVarDecl"),
this);
}

void UseNoexceptCheck::check(const MatchFinder::MatchResult &Result) {
const FunctionProtoType *FnTy = nullptr;
bool DtorOrOperatorDel = false;
SourceRange Range;

if (const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl")) {
DtorOrOperatorDel = Result.Nodes.getNodeAs<FunctionDecl>("del-dtor");
FnTy = FuncDecl->getType()->getAs<FunctionProtoType>();
if (const auto *TSI = FuncDecl->getTypeSourceInfo())
Range =
TSI->getTypeLoc().castAs<FunctionTypeLoc>().getExceptionSpecRange();
} else if (const auto *ParmDecl =
Result.Nodes.getNodeAs<ParmVarDecl>("parmVarDecl")) {
FnTy = ParmDecl->getType()
->getAs<Type>()
->getPointeeType()
->getAs<FunctionProtoType>();

if (const auto *TSI = ParmDecl->getTypeSourceInfo())
Range = TSI->getTypeLoc()
.getNextTypeLoc()
.IgnoreParens()
.castAs<FunctionProtoTypeLoc>()
.getExceptionSpecRange();
}
CharSourceRange CRange = Lexer::makeFileCharRange(
CharSourceRange::getTokenRange(Range), *Result.SourceManager,
Result.Context->getLangOpts());

assert(FnTy && "FunctionProtoType is null.");
bool IsNoThrow = FnTy->isNothrow(*Result.Context);
StringRef ReplacementStr =
IsNoThrow
? NoexceptMacro.empty() ? "noexcept" : NoexceptMacro.c_str()
: NoexceptMacro.empty()
? (DtorOrOperatorDel || UseNoexceptFalse) ? "noexcept(false)"
: ""
: "";

FixItHint FixIt;
if ((IsNoThrow || NoexceptMacro.empty()) && CRange.isValid())
FixIt = FixItHint::CreateReplacement(CRange, ReplacementStr);

diag(Range.getBegin(), "dynamic exception specification '%0' is deprecated; "
"consider %select{using '%2'|removing it}1 instead")
<< Lexer::getSourceText(CRange, *Result.SourceManager,
Result.Context->getLangOpts())
<< ReplacementStr.empty() << ReplacementStr << FixIt;
}

} // namespace modernize
} // namespace tidy
} // namespace clang
49 changes: 49 additions & 0 deletions clang-tools-extra/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 {

/// \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() ;
// 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 NoexceptMacro;
bool UseNoexceptFalse;
};

} // namespace modernize
} // namespace tidy
} // namespace clang

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_NOEXCEPT_H
5 changes: 5 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Expand Up @@ -100,6 +100,11 @@ Improvements to clang-tidy
to remove user-defined make functions from ``push_back`` calls on containers
of custom tuple-like types by providing `TupleTypes` and `TupleMakeFunctions`.

- 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-inefficient-vector-operation
<http://clang.llvm.org/extra/clang-tidy/checks/performance-inefficient-vector-operation.html>`_ check

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Expand Up @@ -135,6 +135,7 @@ Clang-Tidy Checks
modernize-use-emplace
modernize-use-equals-default
modernize-use-equals-delete
modernize-use-noexcept
modernize-use-nullptr
modernize-use-override
modernize-use-transparent-functors
Expand Down
@@ -0,0 +1,90 @@
.. title:: clang-tidy - modernize-use-noexcept

modernize-use-noexcept
======================

This check replaces deprecated dynamic exception specifications with
the appropriate noexcept specification (introduced in C++11). By
default this check will replace ``throw()`` with ``noexcept``,
and ``throw(<exception>[,...])`` or ``throw(...)`` with
``noexcept(false)``.

Example
-------

.. code-block:: c++

void foo() throw();
void bar() throw(int) {}

transforms to:

.. code-block:: c++

void foo() noexcept;
void bar() noexcept(false) {}

Options
-------

.. option:: ReplacementString

Users can use :option:`ReplacementString` to specify a macro to use
instead of ``noexcept``. This is useful when maintaining source code
that uses custom exception specification marking other than
``noexcept``. Fix-it hints will only be generated for non-throwing
specifications.

Example
^^^^^^^

.. code-block:: c++

void bar() throw(int);
void foo() throw();

transforms to:

.. code-block:: c++

void bar() throw(int); // No fix-it generated.
void foo() NOEXCEPT;

if the :option:`ReplacementString` option is set to `NOEXCEPT`.

.. option:: UseNoexceptFalse

Enabled by default, disabling will generate fix-it hints that remove
throwing dynamic exception specs, e.g., ``throw(<something>)``,
completely without providing a replacement text, except for
destructors and delete operators that are ``noexcept(true)`` by
default.

Example
^^^^^^^

.. code-block:: c++

void foo() throw(int) {}

struct bar {
void foobar() throw(int);
void operator delete(void *ptr) throw(int);
void operator delete[](void *ptr) throw(int);
~bar() throw(int);
}
transforms to:

.. code-block:: c++

void foo() {}

struct bar {
void foobar();
void operator delete(void *ptr) noexcept(false);
void operator delete[](void *ptr) noexcept(false);
~bar() noexcept(false);
}
if the :option:`UseNoexceptFalse` option is set to `0`.
36 changes: 36 additions & 0 deletions clang-tools-extra/test/clang-tidy/modernize-use-noexcept-macro.cpp
@@ -0,0 +1,36 @@
// 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: dynamic exception specification 'throw()' is deprecated; consider using 'NOEXCEPT' instead [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: dynamic exception specification 'throw(A, B)' is deprecated; consider removing it instead [modernize-use-noexcept]

// Should not trigger a replacement.
void foo() noexcept(true);

struct Z {
void operator delete(void *ptr) throw();
void operator delete[](void *ptr) throw(int);
~Z() throw(int) {}
};
// CHECK-MESSAGES: :[[@LINE-4]]:35: warning: dynamic exception specification 'throw()' is deprecated; consider using 'NOEXCEPT' instead [modernize-use-noexcept]
// CHECK-MESSAGES: :[[@LINE-4]]:37: warning: dynamic exception specification 'throw(int)' is deprecated; consider removing it instead [modernize-use-noexcept]
// CHECK-MESSAGES: :[[@LINE-4]]:8: warning: dynamic exception specification 'throw(int)' is deprecated; consider removing it instead [modernize-use-noexcept]
// CHECK-FIXES: void operator delete(void *ptr) NOEXCEPT;
// CHECK-FIXES: void operator delete[](void *ptr) throw(int);
// CHECK-FIXES: ~Z() throw(int) {}

0 comments on commit 08936e4

Please sign in to comment.