diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidConstOrRefDataMembersCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidConstOrRefDataMembersCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidConstOrRefDataMembersCheck.h @@ -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 diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidConstOrRefDataMembersCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidConstOrRefDataMembersCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidConstOrRefDataMembersCheck.cpp @@ -0,0 +1,37 @@ +//===--- 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("ref")) + diag(MatchedDecl->getLocation(), "member %0 is a reference") << MatchedDecl; + if (const auto *MatchedDecl = Result.Nodes.getNodeAs("const")) + diag(MatchedDecl->getLocation(), "member %0 is const qualified") + << MatchedDecl; +} + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt @@ -4,6 +4,7 @@ ) add_clang_library(clangTidyCppCoreGuidelinesModule + AvoidConstOrRefDataMembersCheck.cpp AvoidGotoCheck.cpp AvoidNonConstGlobalVariablesCheck.cpp CppCoreGuidelinesTidyModule.cpp diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp @@ -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 @@ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck( "cppcoreguidelines-avoid-c-arrays"); + CheckFactories.registerCheck( + "cppcoreguidelines-avoid-const-or-ref-data-members"); CheckFactories.registerCheck( "cppcoreguidelines-avoid-goto"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -127,6 +127,11 @@ Detects confusable Unicode identifiers. +- New :doc:`cppcoreguidelines-avoid-const-or-ref-data-members + ` check. + + Warns when a struct or class uses const or reference (lvalue or rvalue) data members. + - New :doc:`modernize-macro-to-enum ` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-const-or-ref-data-members.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-const-or-ref-data-members.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-const-or-ref-data-members.rst @@ -0,0 +1,48 @@ +.. 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; // Alternative 1 + gsl::not_null x; // Alternative 2 + std::reference_wrapper x; // Alternative 3 + }; + + // Bad, rvalue reference member + struct RefRef { + int&& x; + }; + +The check implements +`rule C.12 of C++ Core Guidelines `_. + +Further reading: +`Data members: Never const `_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -175,6 +175,7 @@ `clang-analyzer-valist.Unterminated `_, `concurrency-mt-unsafe `_, `concurrency-thread-canceltype-asynchronous `_, + `cppcoreguidelines-avoid-const-or-ref-data-members `_, `cppcoreguidelines-avoid-goto `_, `cppcoreguidelines-avoid-non-const-global-variables `_, `cppcoreguidelines-init-variables `_, "Yes" diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/cppcoreguidelines-avoid-const-or-ref-data-members.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/cppcoreguidelines-avoid-const-or-ref-data-members.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/cppcoreguidelines-avoid-const-or-ref-data-members.cpp @@ -0,0 +1,152 @@ +// RUN: %check_clang_tidy %s cppcoreguidelines-avoid-const-or-ref-data-members %t +namespace std { +template +struct reference_wrapper {}; +} // namespace std + +namespace gsl { +template +struct not_null {}; +} // namespace gsl + +struct Ok { + int i; + int *p; + const int *pc; + std::reference_wrapper r; + gsl::not_null n; +}; + +struct ConstMember { + const int c; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: member 'c' is const qualified [cppcoreguidelines-avoid-const-or-ref-data-members] +}; + +struct LvalueRefMember { + int &lr; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: member 'lr' is a reference +}; + +struct ConstRefMember { + const int &cr; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: member 'cr' is a reference +}; + +struct RvalueRefMember { + int &&rr; + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: member 'rr' is a reference +}; + +struct ConstAndRefMembers { + int const c; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: member 'c' is const qualified + int &lr; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: member 'lr' is a reference + const int &cr; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: member 'cr' is a reference + int &&rr; + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: member 'rr' is a reference +}; + +struct Foo {}; + +struct Ok2 { + Foo i; + Foo *p; + const Foo *pc; + std::reference_wrapper r; + gsl::not_null n; +}; + +struct ConstMember2 { + const Foo c; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: member 'c' is const qualified +}; + +struct LvalueRefMember2 { + Foo &lr; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: member 'lr' is a reference +}; + +struct ConstRefMember2 { + const Foo &cr; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: member 'cr' is a reference +}; + +struct RvalueRefMember2 { + Foo &&rr; + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: member 'rr' is a reference +}; + +struct ConstAndRefMembers2 { + const Foo c; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: member 'c' is const qualified + Foo &lr; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: member 'lr' is a reference + const Foo &cr; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: member 'cr' is a reference + Foo &&rr; + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: member 'rr' 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' is const qualified + RefType lr; + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: member 'lr' is a reference + ConstRefType cr; + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: member 'cr' is a reference + RefRefType rr; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: member 'rr' is a reference +}; + +template +using Array = int[N]; + +struct ConstArrayMember { + const Array<1> c; + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: member 'c' is const qualified +}; + +struct LvalueRefArrayMember { + Array<2> &lr; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: member 'lr' is a reference +}; + +struct ConstLvalueRefArrayMember { + Array<3> const &cr; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: member 'cr' is a reference +}; + +struct RvalueRefArrayMember { + Array<4> &&rr; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: member 'rr' is a reference +}; + +template +struct TemplatedOk { + T t; +}; + +template +struct TemplatedConst { + T t; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: member 't' is const qualified +}; + +template +struct TemplatedRef { + T t; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: member 't' is a reference +}; + +TemplatedOk t1{}; +TemplatedConst t2{123}; +TemplatedRef t3{123}; +TemplatedRef t4{123}; +TemplatedRef t5{t1.t};