xref: /llvm-project/clang-tools-extra/clang-tidy/misc/NonPrivateMemberVariablesInClassesCheck.cpp (revision 7d2ea6c422d3f5712b7253407005e1a465a76946)
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::tidy::misc {
16 
17 namespace {
18 
AST_MATCHER(CXXRecordDecl,hasMethods)19 AST_MATCHER(CXXRecordDecl, hasMethods) {
20   return std::distance(Node.method_begin(), Node.method_end()) != 0;
21 }
22 
AST_MATCHER(CXXRecordDecl,hasNonStaticNonImplicitMethod)23 AST_MATCHER(CXXRecordDecl, hasNonStaticNonImplicitMethod) {
24   return hasMethod(unless(anyOf(isStaticStorageClass(), isImplicit())))
25       .matches(Node, Finder, Builder);
26 }
27 
AST_MATCHER(CXXRecordDecl,hasNonPublicMemberVariable)28 AST_MATCHER(CXXRecordDecl, hasNonPublicMemberVariable) {
29   return cxxRecordDecl(has(fieldDecl(unless(isPublic()))))
30       .matches(Node, Finder, Builder);
31 }
32 
AST_POLYMORPHIC_MATCHER_P(boolean,AST_POLYMORPHIC_SUPPORTED_TYPES (Stmt,Decl),bool,Boolean)33 AST_POLYMORPHIC_MATCHER_P(boolean, AST_POLYMORPHIC_SUPPORTED_TYPES(Stmt, Decl),
34                           bool, Boolean) {
35   return Boolean;
36 }
37 
38 } // namespace
39 
40 NonPrivateMemberVariablesInClassesCheck::
NonPrivateMemberVariablesInClassesCheck(StringRef Name,ClangTidyContext * Context)41     NonPrivateMemberVariablesInClassesCheck(StringRef Name,
42                                             ClangTidyContext *Context)
43     : ClangTidyCheck(Name, Context),
44       IgnoreClassesWithAllMemberVariablesBeingPublic(
45           Options.get("IgnoreClassesWithAllMemberVariablesBeingPublic", false)),
46       IgnorePublicMemberVariables(
47           Options.get("IgnorePublicMemberVariables", false)) {}
48 
storeOptions(ClangTidyOptions::OptionMap & Opts)49 void NonPrivateMemberVariablesInClassesCheck::storeOptions(
50     ClangTidyOptions::OptionMap &Opts) {
51   Options.store(Opts, "IgnoreClassesWithAllMemberVariablesBeingPublic",
52                 IgnoreClassesWithAllMemberVariablesBeingPublic);
53   Options.store(Opts, "IgnorePublicMemberVariables",
54                 IgnorePublicMemberVariables);
55 }
56 
registerMatchers(MatchFinder * Finder)57 void NonPrivateMemberVariablesInClassesCheck::registerMatchers(
58     MatchFinder *Finder) {
59   // We can ignore structs/classes with all member variables being public.
60   auto ShouldIgnoreRecord =
61       allOf(boolean(IgnoreClassesWithAllMemberVariablesBeingPublic),
62             unless(hasNonPublicMemberVariable()));
63 
64   // There are three visibility types: public, protected, private.
65   // If we are ok with public fields, then we only want to complain about
66   // protected fields, else we want to complain about all non-private fields.
67   // We can ignore public member variables in structs/classes, in unions.
68   auto InterestingField = IgnorePublicMemberVariables
69                               ? fieldDecl(isProtected())
70                               : fieldDecl(unless(isPrivate()));
71 
72   // We only want the records that not only contain the mutable data (non-static
73   // member variables), but also have some logic (non-static, non-implicit
74   // member functions).  We may optionally ignore records where all the member
75   // variables are public.
76   Finder->addMatcher(cxxRecordDecl(anyOf(isStruct(), isClass()), hasMethods(),
77                                    hasNonStaticNonImplicitMethod(),
78                                    unless(ShouldIgnoreRecord),
79                                    forEach(InterestingField.bind("field")))
80                          .bind("record"),
81                      this);
82 }
83 
check(const MatchFinder::MatchResult & Result)84 void NonPrivateMemberVariablesInClassesCheck::check(
85     const MatchFinder::MatchResult &Result) {
86   const auto *Field = Result.Nodes.getNodeAs<FieldDecl>("field");
87   assert(Field && "We should have the field we are going to complain about");
88 
89   diag(Field->getLocation(), "member variable %0 has %1 visibility")
90       << Field << Field->getAccess();
91 }
92 
93 } // namespace clang::tidy::misc
94