xref: /llvm-project/clang-tools-extra/clang-tidy/misc/NonPrivateMemberVariablesInClassesCheck.cpp (revision 61c818f25363f8505b38010b475b66bbf5d321c8)
1 //===--- NonPrivateMemberVariablesInClassesCheck.cpp - clang-tidy ---------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "NonPrivateMemberVariablesInClassesCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 
13 using namespace clang::ast_matchers;
14 
15 namespace clang {
16 namespace tidy {
17 namespace misc {
18 
19 namespace {
20 
21 AST_MATCHER(CXXRecordDecl, hasMethods) {
22   return std::distance(Node.method_begin(), Node.method_end()) != 0;
23 }
24 
25 AST_MATCHER(CXXRecordDecl, hasNonStaticNonImplicitMethod) {
26   return hasMethod(unless(anyOf(isStaticStorageClass(), isImplicit())))
27       .matches(Node, Finder, Builder);
28 }
29 
30 AST_MATCHER(CXXRecordDecl, hasNonPublicMemberVariable) {
31   return cxxRecordDecl(has(fieldDecl(unless(isPublic()))))
32       .matches(Node, Finder, Builder);
33 }
34 
35 AST_POLYMORPHIC_MATCHER_P(boolean, AST_POLYMORPHIC_SUPPORTED_TYPES(Stmt, Decl),
36                           bool, Boolean) {
37   return Boolean;
38 }
39 
40 } // namespace
41 
42 NonPrivateMemberVariablesInClassesCheck::
43     NonPrivateMemberVariablesInClassesCheck(StringRef Name,
44                                             ClangTidyContext *Context)
45     : ClangTidyCheck(Name, Context),
46       IgnoreClassesWithAllMemberVariablesBeingPublic(
47           Options.get("IgnoreClassesWithAllMemberVariablesBeingPublic", false)),
48       IgnorePublicMemberVariables(
49           Options.get("IgnorePublicMemberVariables", false)) {}
50 
51 void NonPrivateMemberVariablesInClassesCheck::registerMatchers(
52     MatchFinder *Finder) {
53   if (!getLangOpts().CPlusPlus)
54     return;
55 
56   // We can ignore structs/classes with all member variables being public.
57   auto ShouldIgnoreRecord =
58       allOf(boolean(IgnoreClassesWithAllMemberVariablesBeingPublic),
59             unless(hasNonPublicMemberVariable()));
60 
61   // There are three visibility types: public, protected, private.
62   // If we are ok with public fields, then we only want to complain about
63   // protected fields, else we want to complain about all non-private fields.
64   // We can ignore public member variables in structs/classes, in unions.
65   auto InterestingField = fieldDecl(
66       IgnorePublicMemberVariables ? isProtected() : unless(isPrivate()));
67 
68   // We only want the records that not only contain the mutable data (non-static
69   // member variables), but also have some logic (non-static, non-implicit
70   // member functions).  We may optionally ignore records where all the member
71   // variables are public.
72   Finder->addMatcher(cxxRecordDecl(anyOf(isStruct(), isClass()), hasMethods(),
73                                    hasNonStaticNonImplicitMethod(),
74                                    unless(ShouldIgnoreRecord),
75                                    forEach(InterestingField.bind("field")))
76                          .bind("record"),
77                      this);
78 }
79 
80 void NonPrivateMemberVariablesInClassesCheck::check(
81     const MatchFinder::MatchResult &Result) {
82   const auto *Field = Result.Nodes.getNodeAs<FieldDecl>("field");
83   assert(Field && "We should have the field we are going to complain about");
84 
85   diag(Field->getLocation(), "member variable %0 has %1 visibility")
86       << Field << Field->getAccess();
87 }
88 
89 } // namespace misc
90 } // namespace tidy
91 } // namespace clang
92