[clang-tidy] Add cppcoreguidelines-avoid-const-or-ref-data-members check

Flags uses of const-qualified and reference data members in structs.
Implements rule C.12 of C++ Core Guidelines.

Differential Revision: https://reviews.llvm.org/D126880
This commit is contained in:
Carlos Galvez
2022-06-02 13:10:07 +00:00
parent 2cb51449f0
commit 9ae5896d96
8 changed files with 304 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
//===--- AvoidConstOrRefDataMembersCheck.cpp - clang-tidy -----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "AvoidConstOrRefDataMembersCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
void AvoidConstOrRefDataMembersCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
fieldDecl(hasType(hasCanonicalType(referenceType()))).bind("ref"), this);
Finder->addMatcher(
fieldDecl(hasType(qualType(isConstQualified()))).bind("const"), this);
}
void AvoidConstOrRefDataMembersCheck::check(
const MatchFinder::MatchResult &Result) {
if (const auto *MatchedDecl = Result.Nodes.getNodeAs<FieldDecl>("ref"))
diag(MatchedDecl->getLocation(), "member %0 of type %1 is a reference")
<< MatchedDecl << MatchedDecl->getType();
if (const auto *MatchedDecl = Result.Nodes.getNodeAs<FieldDecl>("const"))
diag(MatchedDecl->getLocation(), "member %0 of type %1 is const qualified")
<< MatchedDecl << MatchedDecl->getType();
}
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,38 @@
//===--- AvoidConstOrRefDataMembersCheck.h - clang-tidy ---------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_AVOIDCONSTORREFDATAMEMBERSCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_AVOIDCONSTORREFDATAMEMBERSCHECK_H
#include "../ClangTidyCheck.h"
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
/// Const-qualified or reference data members in classes should be avoided, as
/// they make the class non-copy-assignable.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/avoid-const-or-ref-data-members.html
class AvoidConstOrRefDataMembersCheck : public ClangTidyCheck {
public:
AvoidConstOrRefDataMembersCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_AVOIDCONSTORREFDATAMEMBERSCHECK_H

View File

@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
)
add_clang_library(clangTidyCppCoreGuidelinesModule
AvoidConstOrRefDataMembersCheck.cpp
AvoidGotoCheck.cpp
AvoidNonConstGlobalVariablesCheck.cpp
CppCoreGuidelinesTidyModule.cpp

View File

@@ -14,6 +14,7 @@
#include "../modernize/AvoidCArraysCheck.h"
#include "../modernize/UseOverrideCheck.h"
#include "../readability/MagicNumbersCheck.h"
#include "AvoidConstOrRefDataMembersCheck.h"
#include "AvoidGotoCheck.h"
#include "AvoidNonConstGlobalVariablesCheck.h"
#include "InitVariablesCheck.h"
@@ -47,6 +48,8 @@ public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<modernize::AvoidCArraysCheck>(
"cppcoreguidelines-avoid-c-arrays");
CheckFactories.registerCheck<AvoidConstOrRefDataMembersCheck>(
"cppcoreguidelines-avoid-const-or-ref-data-members");
CheckFactories.registerCheck<AvoidGotoCheck>(
"cppcoreguidelines-avoid-goto");
CheckFactories.registerCheck<readability::MagicNumbersCheck>(

View File

@@ -99,6 +99,11 @@ Improvements to clang-tidy
New checks
^^^^^^^^^^
- New :doc:`cppcoreguidelines-avoid-const-or-ref-data-members
<clang-tidy/checks/cppcoreguidelines/avoid-const-or-ref-data-members>` check.
Warns when a struct or class uses const or reference (lvalue or rvalue) data members.
New check aliases
^^^^^^^^^^^^^^^^^

View File

@@ -0,0 +1,49 @@
.. title:: clang-tidy - cppcoreguidelines-avoid-const-or-ref-data-members
cppcoreguidelines-avoid-const-or-ref-data-members
=================================================
This check warns when structs or classes have const-qualified or reference
(lvalue or rvalue) data members. Having such members is rarely useful, and
makes the class only copy-constructible but not copy-assignable.
Examples:
.. code-block:: c++
// Bad, const-qualified member
struct Const {
const int x;
}
// Good:
class Foo {
public:
int get() const { return x; }
private:
int x;
};
// Bad, lvalue reference member
struct Ref {
int& x;
};
// Good:
struct Foo {
int* x;
std::unique_ptr<int> x;
std::shared_ptr<int> x;
gsl::not_null<int> x;
};
// Bad, rvalue reference member
struct RefRef {
int&& x;
};
The check implements
`rule C.12 of C++ Core Guidelines <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c12-dont-make-data-members-const-or-references>`_.
Further reading:
`Data members: Never const <https://quuxplusone.github.io/blog/2022/01/23/dont-const-all-the-things/#data-members-never-const>`_.

View File

@@ -176,6 +176,7 @@ Clang-Tidy Checks
`clang-analyzer-valist.Unterminated <clang-analyzer/valist.Unterminated.html>`_,
`concurrency-mt-unsafe <concurrency/mt-unsafe.html>`_,
`concurrency-thread-canceltype-asynchronous <concurrency/thread-canceltype-asynchronous.html>`_,
`cppcoreguidelines-avoid-const-or-ref-data-members <cppcoreguidelines/avoid-const-or-ref-data-members.html>`_,
`cppcoreguidelines-avoid-goto <cppcoreguidelines/avoid-goto.html>`_,
`cppcoreguidelines-avoid-non-const-global-variables <cppcoreguidelines/avoid-non-const-global-variables.html>`_,
`cppcoreguidelines-init-variables <cppcoreguidelines/init-variables.html>`_, "Yes"

View File

@@ -0,0 +1,169 @@
// RUN: %check_clang_tidy %s cppcoreguidelines-avoid-const-or-ref-data-members %t
namespace std {
template <typename T>
struct unique_ptr {};
template <typename T>
struct shared_ptr {};
} // namespace std
namespace gsl {
template <typename T>
struct not_null {};
} // namespace gsl
struct Ok {
int i;
int *p;
const int *pc;
std::unique_ptr<int> up;
std::shared_ptr<int> sp;
gsl::not_null<int> n;
};
struct ConstMember {
const int c;
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: member 'c' of type 'const int' is const qualified [cppcoreguidelines-avoid-const-or-ref-data-members]
};
struct LvalueRefMember {
int &lr;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: member 'lr' of type 'int &' is a reference
};
struct ConstRefMember {
const int &cr;
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: member 'cr' of type 'const int &' is a reference
};
struct RvalueRefMember {
int &&rr;
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: member 'rr' of type 'int &&' is a reference
};
struct ConstAndRefMembers {
const int c;
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: member 'c' of type 'const int' is const qualified
int &lr;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: member 'lr' of type 'int &' is a reference
const int &cr;
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: member 'cr' of type 'const int &' is a reference
int &&rr;
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: member 'rr' of type 'int &&' is a reference
};
struct Foo {};
struct Ok2 {
Foo i;
Foo *p;
const Foo *pc;
std::unique_ptr<Foo> up;
std::shared_ptr<Foo> sp;
gsl::not_null<Foo> n;
};
struct ConstMember2 {
const Foo c;
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: member 'c' of type 'const Foo' is const qualified
};
struct LvalueRefMember2 {
Foo &lr;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: member 'lr' of type 'Foo &' is a reference
};
struct ConstRefMember2 {
const Foo &cr;
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: member 'cr' of type 'const Foo &' is a reference
};
struct RvalueRefMember2 {
Foo &&rr;
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: member 'rr' of type 'Foo &&' is a reference
};
struct ConstAndRefMembers2 {
const Foo c;
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: member 'c' of type 'const Foo' is const qualified
Foo &lr;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: member 'lr' of type 'Foo &' is a reference
const Foo &cr;
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: member 'cr' of type 'const Foo &' is a reference
Foo &&rr;
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: member 'rr' of type 'Foo &&' is a reference
};
using ConstType = const int;
using RefType = int &;
using ConstRefType = const int &;
using RefRefType = int &&;
struct WithAlias {
ConstType c;
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: member 'c' of type 'ConstType' (aka 'const int') is const qualified
RefType lr;
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: member 'lr' of type 'RefType' (aka 'int &') is a reference
ConstRefType cr;
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: member 'cr' of type 'ConstRefType' (aka 'const int &') is a reference
RefRefType rr;
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: member 'rr' of type 'RefRefType' (aka 'int &&') is a reference
};
template <int N>
using Array = int[N];
struct ConstArrayMember {
const Array<1> c;
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: member 'c' of type 'const Array<1>' (aka 'const int[1]') is const qualified
};
struct LvalueRefArrayMember {
Array<2> &lr;
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: member 'lr' of type 'Array<2> &' (aka 'int (&)[2]') is a reference
};
struct ConstLvalueRefArrayMember {
const Array<3> &cr;
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: member 'cr' of type 'const Array<3> &' (aka 'const int (&)[3]') is a reference
};
struct RvalueRefArrayMember {
Array<4> &&rr;
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: member 'rr' of type 'Array<4> &&' (aka 'int (&&)[4]') is a reference
};
template <typename T>
struct TemplatedOk {
T t;
};
template <typename T>
struct TemplatedConst {
T t;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: member 't' of type 'const int' is const qualified
};
template <typename T>
struct TemplatedConstRef {
T t;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: member 't' of type 'const int &' is a reference
};
template <typename T>
struct TemplatedRefRef {
T t;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: member 't' of type 'int &&' is a reference
};
template <typename T>
struct TemplatedRef {
T t;
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: member 't' of type 'int &' is a reference
};
TemplatedOk<int> t1{};
TemplatedConst<const int> t2{123};
TemplatedConstRef<const int &> t3{123};
TemplatedRefRef<int &&> t4{123};
TemplatedRef<int &> t5{t1.t};