Skip to content

Commit

Permalink
[clang-tidy] Add modernize-use-equals-delete check
Browse files Browse the repository at this point in the history
Summary: Fixes PR27872

Reviewers: klimek, hokein, alexfh, aaron.ballman

Subscribers: Prazek, Eugene.Zelenko, danielmarjamaki, cfe-commits, mgorny

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

llvm-svn: 286472
  • Loading branch information
pepsiman committed Nov 10, 2016
1 parent ee187fd commit e293eab
Show file tree
Hide file tree
Showing 9 changed files with 289 additions and 2 deletions.
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
Expand Up @@ -18,6 +18,7 @@ add_clang_library(clangTidyModernizeModule
UseBoolLiteralsCheck.cpp
UseDefaultCheck.cpp
UseEmplaceCheck.cpp
UseEqualsDeleteCheck.cpp
UseNullptrCheck.cpp
UseOverrideCheck.cpp
UseUsingCheck.cpp
Expand Down
Expand Up @@ -24,6 +24,7 @@
#include "UseBoolLiteralsCheck.h"
#include "UseDefaultCheck.h"
#include "UseEmplaceCheck.h"
#include "UseEqualsDeleteCheck.h"
#include "UseNullptrCheck.h"
#include "UseOverrideCheck.h"
#include "UseUsingCheck.h"
Expand Down Expand Up @@ -56,6 +57,8 @@ class ModernizeModule : public ClangTidyModule {
"modernize-use-bool-literals");
CheckFactories.registerCheck<UseDefaultCheck>("modernize-use-default");
CheckFactories.registerCheck<UseEmplaceCheck>("modernize-use-emplace");
CheckFactories.registerCheck<UseEqualsDeleteCheck>(
"modernize-use-equals-delete");
CheckFactories.registerCheck<UseNullptrCheck>("modernize-use-nullptr");
CheckFactories.registerCheck<UseOverrideCheck>("modernize-use-override");
CheckFactories.registerCheck<UseUsingCheck>("modernize-use-using");
Expand Down
69 changes: 69 additions & 0 deletions clang-tools-extra/clang-tidy/modernize/UseEqualsDeleteCheck.cpp
@@ -0,0 +1,69 @@
//===--- UseEqualsDeleteCheck.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 "UseEqualsDeleteCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"

using namespace clang::ast_matchers;

namespace clang {
namespace tidy {
namespace modernize {

static const char SpecialFunction[] = "SpecialFunction";
static const char DeletedNotPublic[] = "DeletedNotPublic";

void UseEqualsDeleteCheck::registerMatchers(MatchFinder *Finder) {
auto PrivateSpecialFn = cxxMethodDecl(
isPrivate(),
anyOf(cxxConstructorDecl(anyOf(isDefaultConstructor(),
isCopyConstructor(), isMoveConstructor())),
cxxMethodDecl(
anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())),
cxxDestructorDecl()));

Finder->addMatcher(
cxxMethodDecl(
PrivateSpecialFn,
unless(anyOf(hasBody(stmt()), isDefaulted(), isDeleted(),
// Ensure that all methods except private special member
// functions are defined.
hasParent(cxxRecordDecl(hasMethod(unless(
anyOf(PrivateSpecialFn, hasBody(stmt()), isPure(),
isDefaulted(), isDeleted()))))))))
.bind(SpecialFunction),
this);

Finder->addMatcher(
cxxMethodDecl(isDeleted(), unless(isPublic())).bind(DeletedNotPublic),
this);
}

void UseEqualsDeleteCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *Func =
Result.Nodes.getNodeAs<CXXMethodDecl>(SpecialFunction)) {
SourceLocation EndLoc = Lexer::getLocForEndOfToken(
Func->getLocEnd(), 0, *Result.SourceManager, getLangOpts());

// FIXME: Improve FixItHint to make method public
diag(Func->getLocation(),
"use '= delete' to prohibit calling of a special member function")
<< FixItHint::CreateInsertion(EndLoc, " = delete");
} else if (const auto *Func =
Result.Nodes.getNodeAs<CXXMethodDecl>(DeletedNotPublic)) {
// FIXME: Add FixItHint to make method public
diag(Func->getLocation(), "deleted member function should be public");
}
}

} // namespace modernize
} // namespace tidy
} // namespace clang
50 changes: 50 additions & 0 deletions clang-tools-extra/clang-tidy/modernize/UseEqualsDeleteCheck.h
@@ -0,0 +1,50 @@
//===--- UseEqualsDeleteCheck.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_EQUALS_DELETE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EQUALS_DELETE_H

#include "../ClangTidy.h"

namespace clang {
namespace tidy {
namespace modernize {

/// \brief Mark unimplemented private special member functions with '= delete'.
/// \code
/// struct A {
/// private:
/// A(const A&);
/// A& operator=(const A&);
/// };
/// \endcode
/// Is converted to:
/// \code
/// struct A {
/// private:
/// A(const A&) = delete;
/// A& operator=(const A&) = delete;
/// };
/// \endcode
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-equals-delete.html
class UseEqualsDeleteCheck : public ClangTidyCheck {
public:
UseEqualsDeleteCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};

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

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EQUALS_DELETE_H
5 changes: 5 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Expand Up @@ -91,6 +91,11 @@ Improvements to clang-tidy
<http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-auto.html>`_ check
now warns about variable declarations that are initialized with a cast.

- New `modernize-use-equals-delete
<http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-equals-delete.html>`_ check

Adds ``= delete`` to unimplemented private special member functions.

- New `mpi-buffer-deref
<http://clang.llvm.org/extra/clang-tidy/checks/mpi-buffer-deref.html>`_ check

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Expand Up @@ -111,6 +111,7 @@ Clang-Tidy Checks
modernize-use-bool-literals
modernize-use-default
modernize-use-emplace
modernize-use-equals-delete
modernize-use-nullptr
modernize-use-override
modernize-use-using
Expand Down
Expand Up @@ -25,5 +25,4 @@ defaulted functions as trivial.
A::~A() = default;

.. note::
Copy-constructor, copy-assignment operator, move-constructor and
move-assignment operator are not supported yet.
Move-constructor and move-assignment operator are not supported yet.
@@ -0,0 +1,25 @@
.. title:: clang-tidy - modernize-use-equals-delete

modernize-use-equals-delete
===========================

This check marks unimplemented private special member functions with ``= delete``.
To avoid false-positives, this check only applies in a translation unit that has
all other member functions implemented.

.. code-block:: c++

struct A {
private:
A(const A&);
A& operator=(const A&);
};

// becomes

struct A {
private:
A(const A&) = delete;
A& operator=(const A&) = delete;
};

134 changes: 134 additions & 0 deletions clang-tools-extra/test/clang-tidy/modernize-use-equals-delete.cpp
@@ -0,0 +1,134 @@
// RUN: %check_clang_tidy %s modernize-use-equals-delete %t

struct PositivePrivate {
private:
PositivePrivate();
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
// CHECK-FIXES: PositivePrivate() = delete;
PositivePrivate(const PositivePrivate &);
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
// CHECK-FIXES: PositivePrivate(const PositivePrivate &) = delete;
PositivePrivate &operator=(const PositivePrivate &);
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
// CHECK-FIXES: PositivePrivate &operator=(const PositivePrivate &) = delete;
PositivePrivate(PositivePrivate &&);
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
// CHECK-FIXES: PositivePrivate(PositivePrivate &&) = delete;
PositivePrivate &operator=(PositivePrivate &&);
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
// CHECK-FIXES: PositivePrivate &operator=(PositivePrivate &&) = delete;
~PositivePrivate();
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
// CHECK-FIXES: ~PositivePrivate() = delete;
};

struct NegativePublic {
NegativePublic(const NegativePublic &);
};

struct NegativeProtected {
protected:
NegativeProtected(const NegativeProtected &);
};

struct PositiveInlineMember {
int foo() { return 0; }

private:
PositiveInlineMember(const PositiveInlineMember &);
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
// CHECK-FIXES: PositiveInlineMember(const PositiveInlineMember &) = delete;
};

struct PositiveOutOfLineMember {
int foo();

private:
PositiveOutOfLineMember(const PositiveOutOfLineMember &);
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
// CHECK-FIXES: PositiveOutOfLineMember(const PositiveOutOfLineMember &) = delete;
};

int PositiveOutOfLineMember::foo() { return 0; }

struct PositiveAbstractMember {
virtual int foo() = 0;

private:
PositiveAbstractMember(const PositiveAbstractMember &);
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
// CHECK-FIXES: PositiveAbstractMember(const PositiveAbstractMember &) = delete;
};

struct NegativeMemberNotImpl {
int foo();

private:
NegativeMemberNotImpl(const NegativeMemberNotImpl &);
};

struct NegativeStaticMemberNotImpl {
static int foo();

private:
NegativeStaticMemberNotImpl(const NegativeStaticMemberNotImpl &);
};

struct NegativeInline {
private:
NegativeInline(const NegativeInline &) {}
};

struct NegativeOutOfLine {
private:
NegativeOutOfLine(const NegativeOutOfLine &);
};

NegativeOutOfLine::NegativeOutOfLine(const NegativeOutOfLine &) {}

struct NegativeConstructNotImpl {
NegativeConstructNotImpl();

private:
NegativeConstructNotImpl(const NegativeConstructNotImpl &);
};

struct PositiveDefaultedConstruct {
PositiveDefaultedConstruct() = default;

private:
PositiveDefaultedConstruct(const PositiveDefaultedConstruct &);
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
// CHECK-FIXES: PositiveDefaultedConstruct(const PositiveDefaultedConstruct &) = delete;
};

struct PositiveDeletedConstruct {
PositiveDeletedConstruct() = delete;

private:
PositiveDeletedConstruct(const PositiveDeletedConstruct &);
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
// CHECK-FIXES: PositiveDeletedConstruct(const PositiveDeletedConstruct &) = delete;
};

struct NegativeDefaulted {
private:
NegativeDefaulted(const NegativeDefaulted &) = default;
};

struct PrivateDeleted {
private:
PrivateDeleted(const PrivateDeleted &) = delete;
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: deleted member function should be public [modernize-use-equals-delete]
};

struct ProtectedDeleted {
protected:
ProtectedDeleted(const ProtectedDeleted &) = delete;
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: deleted member function should be public [modernize-use-equals-delete]
};

struct PublicDeleted {
public:
PublicDeleted(const PublicDeleted &) = delete;
};

0 comments on commit e293eab

Please sign in to comment.