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