xref: /llvm-project/clang-tools-extra/clang-tidy/readability/RedundantAccessSpecifiersCheck.cpp (revision 7d2ea6c422d3f5712b7253407005e1a465a76946)
1 //===--- RedundantAccessSpecifiersCheck.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 "RedundantAccessSpecifiersCheck.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::readability {
16 
registerMatchers(MatchFinder * Finder)17 void RedundantAccessSpecifiersCheck::registerMatchers(MatchFinder *Finder) {
18   Finder->addMatcher(
19       cxxRecordDecl(has(accessSpecDecl())).bind("redundant-access-specifiers"),
20       this);
21 }
22 
check(const MatchFinder::MatchResult & Result)23 void RedundantAccessSpecifiersCheck::check(
24     const MatchFinder::MatchResult &Result) {
25   const auto *MatchedDecl =
26       Result.Nodes.getNodeAs<CXXRecordDecl>("redundant-access-specifiers");
27 
28   const AccessSpecDecl *LastASDecl = nullptr;
29   for (DeclContext::specific_decl_iterator<AccessSpecDecl>
30            AS(MatchedDecl->decls_begin()),
31        ASEnd(MatchedDecl->decls_end());
32        AS != ASEnd; ++AS) {
33     const AccessSpecDecl *ASDecl = *AS;
34 
35     // Ignore macro expansions.
36     if (ASDecl->getLocation().isMacroID()) {
37       LastASDecl = ASDecl;
38       continue;
39     }
40 
41     if (LastASDecl == nullptr) {
42       // First declaration.
43       LastASDecl = ASDecl;
44 
45       if (CheckFirstDeclaration) {
46         AccessSpecifier DefaultSpecifier =
47             MatchedDecl->isClass() ? AS_private : AS_public;
48         if (ASDecl->getAccess() == DefaultSpecifier) {
49           diag(ASDecl->getLocation(),
50                "redundant access specifier has the same accessibility as the "
51                "implicit access specifier")
52               << FixItHint::CreateRemoval(ASDecl->getSourceRange());
53         }
54       }
55 
56       continue;
57     }
58 
59     if (LastASDecl->getAccess() == ASDecl->getAccess()) {
60       // Ignore macro expansions.
61       if (LastASDecl->getLocation().isMacroID()) {
62         LastASDecl = ASDecl;
63         continue;
64       }
65 
66       diag(ASDecl->getLocation(),
67            "redundant access specifier has the same accessibility as the "
68            "previous access specifier")
69           << FixItHint::CreateRemoval(ASDecl->getSourceRange());
70       diag(LastASDecl->getLocation(), "previously declared here",
71            DiagnosticIDs::Note);
72     } else {
73       LastASDecl = ASDecl;
74     }
75   }
76 }
77 
78 } // namespace clang::tidy::readability
79