16cfa38f1SRoman Lebedev //===--- NonPrivateMemberVariablesInClassesCheck.cpp - clang-tidy ---------===//
26cfa38f1SRoman Lebedev //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66cfa38f1SRoman Lebedev //
76cfa38f1SRoman Lebedev //===----------------------------------------------------------------------===//
86cfa38f1SRoman Lebedev
96cfa38f1SRoman Lebedev #include "NonPrivateMemberVariablesInClassesCheck.h"
106cfa38f1SRoman Lebedev #include "clang/AST/ASTContext.h"
116cfa38f1SRoman Lebedev #include "clang/ASTMatchers/ASTMatchFinder.h"
126cfa38f1SRoman Lebedev
136cfa38f1SRoman Lebedev using namespace clang::ast_matchers;
146cfa38f1SRoman Lebedev
15*7d2ea6c4SCarlos Galvez namespace clang::tidy::misc {
166cfa38f1SRoman Lebedev
176cfa38f1SRoman Lebedev namespace {
186cfa38f1SRoman Lebedev
AST_MATCHER(CXXRecordDecl,hasMethods)196cfa38f1SRoman Lebedev AST_MATCHER(CXXRecordDecl, hasMethods) {
206cfa38f1SRoman Lebedev return std::distance(Node.method_begin(), Node.method_end()) != 0;
216cfa38f1SRoman Lebedev }
226cfa38f1SRoman Lebedev
AST_MATCHER(CXXRecordDecl,hasNonStaticNonImplicitMethod)2361c818f2SMiklos Vajna AST_MATCHER(CXXRecordDecl, hasNonStaticNonImplicitMethod) {
2461c818f2SMiklos Vajna return hasMethod(unless(anyOf(isStaticStorageClass(), isImplicit())))
256cfa38f1SRoman Lebedev .matches(Node, Finder, Builder);
266cfa38f1SRoman Lebedev }
276cfa38f1SRoman Lebedev
AST_MATCHER(CXXRecordDecl,hasNonPublicMemberVariable)286cfa38f1SRoman Lebedev AST_MATCHER(CXXRecordDecl, hasNonPublicMemberVariable) {
296cfa38f1SRoman Lebedev return cxxRecordDecl(has(fieldDecl(unless(isPublic()))))
306cfa38f1SRoman Lebedev .matches(Node, Finder, Builder);
316cfa38f1SRoman Lebedev }
326cfa38f1SRoman Lebedev
AST_POLYMORPHIC_MATCHER_P(boolean,AST_POLYMORPHIC_SUPPORTED_TYPES (Stmt,Decl),bool,Boolean)336cfa38f1SRoman Lebedev AST_POLYMORPHIC_MATCHER_P(boolean, AST_POLYMORPHIC_SUPPORTED_TYPES(Stmt, Decl),
346cfa38f1SRoman Lebedev bool, Boolean) {
356cfa38f1SRoman Lebedev return Boolean;
366cfa38f1SRoman Lebedev }
376cfa38f1SRoman Lebedev
386cfa38f1SRoman Lebedev } // namespace
396cfa38f1SRoman Lebedev
406cfa38f1SRoman Lebedev NonPrivateMemberVariablesInClassesCheck::
NonPrivateMemberVariablesInClassesCheck(StringRef Name,ClangTidyContext * Context)416cfa38f1SRoman Lebedev NonPrivateMemberVariablesInClassesCheck(StringRef Name,
426cfa38f1SRoman Lebedev ClangTidyContext *Context)
436cfa38f1SRoman Lebedev : ClangTidyCheck(Name, Context),
446cfa38f1SRoman Lebedev IgnoreClassesWithAllMemberVariablesBeingPublic(
456cfa38f1SRoman Lebedev Options.get("IgnoreClassesWithAllMemberVariablesBeingPublic", false)),
466cfa38f1SRoman Lebedev IgnorePublicMemberVariables(
476cfa38f1SRoman Lebedev Options.get("IgnorePublicMemberVariables", false)) {}
486cfa38f1SRoman Lebedev
storeOptions(ClangTidyOptions::OptionMap & Opts)49db90d315SNathan James void NonPrivateMemberVariablesInClassesCheck::storeOptions(
50db90d315SNathan James ClangTidyOptions::OptionMap &Opts) {
51db90d315SNathan James Options.store(Opts, "IgnoreClassesWithAllMemberVariablesBeingPublic",
52db90d315SNathan James IgnoreClassesWithAllMemberVariablesBeingPublic);
53db90d315SNathan James Options.store(Opts, "IgnorePublicMemberVariables",
54db90d315SNathan James IgnorePublicMemberVariables);
55db90d315SNathan James }
56db90d315SNathan James
registerMatchers(MatchFinder * Finder)576cfa38f1SRoman Lebedev void NonPrivateMemberVariablesInClassesCheck::registerMatchers(
586cfa38f1SRoman Lebedev MatchFinder *Finder) {
596cfa38f1SRoman Lebedev // We can ignore structs/classes with all member variables being public.
606cfa38f1SRoman Lebedev auto ShouldIgnoreRecord =
616cfa38f1SRoman Lebedev allOf(boolean(IgnoreClassesWithAllMemberVariablesBeingPublic),
626cfa38f1SRoman Lebedev unless(hasNonPublicMemberVariable()));
636cfa38f1SRoman Lebedev
646cfa38f1SRoman Lebedev // There are three visibility types: public, protected, private.
656cfa38f1SRoman Lebedev // If we are ok with public fields, then we only want to complain about
666cfa38f1SRoman Lebedev // protected fields, else we want to complain about all non-private fields.
676cfa38f1SRoman Lebedev // We can ignore public member variables in structs/classes, in unions.
681a5c97f3SJan Korous auto InterestingField = IgnorePublicMemberVariables
691a5c97f3SJan Korous ? fieldDecl(isProtected())
701a5c97f3SJan Korous : fieldDecl(unless(isPrivate()));
716cfa38f1SRoman Lebedev
72976e0c07SAlexander Kornienko // We only want the records that not only contain the mutable data (non-static
7361c818f2SMiklos Vajna // member variables), but also have some logic (non-static, non-implicit
7461c818f2SMiklos Vajna // member functions). We may optionally ignore records where all the member
7561c818f2SMiklos Vajna // variables are public.
76976e0c07SAlexander Kornienko Finder->addMatcher(cxxRecordDecl(anyOf(isStruct(), isClass()), hasMethods(),
7761c818f2SMiklos Vajna hasNonStaticNonImplicitMethod(),
78976e0c07SAlexander Kornienko unless(ShouldIgnoreRecord),
796cfa38f1SRoman Lebedev forEach(InterestingField.bind("field")))
806cfa38f1SRoman Lebedev .bind("record"),
816cfa38f1SRoman Lebedev this);
826cfa38f1SRoman Lebedev }
836cfa38f1SRoman Lebedev
check(const MatchFinder::MatchResult & Result)846cfa38f1SRoman Lebedev void NonPrivateMemberVariablesInClassesCheck::check(
856cfa38f1SRoman Lebedev const MatchFinder::MatchResult &Result) {
866cfa38f1SRoman Lebedev const auto *Field = Result.Nodes.getNodeAs<FieldDecl>("field");
876cfa38f1SRoman Lebedev assert(Field && "We should have the field we are going to complain about");
886cfa38f1SRoman Lebedev
896cfa38f1SRoman Lebedev diag(Field->getLocation(), "member variable %0 has %1 visibility")
906cfa38f1SRoman Lebedev << Field << Field->getAccess();
916cfa38f1SRoman Lebedev }
926cfa38f1SRoman Lebedev
93*7d2ea6c4SCarlos Galvez } // namespace clang::tidy::misc
94