xref: /llvm-project/clang-tools-extra/clang-tidy/misc/NonPrivateMemberVariablesInClassesCheck.cpp (revision 1a5c97f3a4b88438b19ff34a285e559e57b1e9d4)
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   // We can ignore structs/classes with all member variables being public.
54   auto ShouldIgnoreRecord =
55       allOf(boolean(IgnoreClassesWithAllMemberVariablesBeingPublic),
56             unless(hasNonPublicMemberVariable()));
57 
58   // There are three visibility types: public, protected, private.
59   // If we are ok with public fields, then we only want to complain about
60   // protected fields, else we want to complain about all non-private fields.
61   // We can ignore public member variables in structs/classes, in unions.
62   auto InterestingField = IgnorePublicMemberVariables
63                               ? fieldDecl(isProtected())
64                               : fieldDecl(unless(isPrivate()));
65 
66   // We only want the records that not only contain the mutable data (non-static
67   // member variables), but also have some logic (non-static, non-implicit
68   // member functions).  We may optionally ignore records where all the member
69   // variables are public.
70   Finder->addMatcher(cxxRecordDecl(anyOf(isStruct(), isClass()), hasMethods(),
71                                    hasNonStaticNonImplicitMethod(),
72                                    unless(ShouldIgnoreRecord),
73                                    forEach(InterestingField.bind("field")))
74                          .bind("record"),
75                      this);
76 }
77 
78 void NonPrivateMemberVariablesInClassesCheck::check(
79     const MatchFinder::MatchResult &Result) {
80   const auto *Field = Result.Nodes.getNodeAs<FieldDecl>("field");
81   assert(Field && "We should have the field we are going to complain about");
82 
83   diag(Field->getLocation(), "member variable %0 has %1 visibility")
84       << Field << Field->getAccess();
85 }
86 
87 } // namespace misc
88 } // namespace tidy
89 } // namespace clang
90