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