151 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "OverrideWithDifferentVisibilityCheck.h"10#include "../utils/Matchers.h"11#include "../utils/OptionsUtils.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13 14using namespace clang::ast_matchers;15using namespace clang;16 17namespace {18 19AST_MATCHER(NamedDecl, isOperatorDecl) {20 const DeclarationName::NameKind NK = Node.getDeclName().getNameKind();21 return NK != DeclarationName::Identifier &&22 NK != DeclarationName::CXXConstructorName &&23 NK != DeclarationName::CXXDestructorName;24}25 26} // namespace27 28namespace clang::tidy {29 30template <>31struct OptionEnumMapping<32 misc::OverrideWithDifferentVisibilityCheck::ChangeKind> {33 static llvm::ArrayRef<std::pair<34 misc::OverrideWithDifferentVisibilityCheck::ChangeKind, StringRef>>35 getEnumMapping() {36 static constexpr std::pair<37 misc::OverrideWithDifferentVisibilityCheck::ChangeKind, StringRef>38 Mapping[] = {39 {misc::OverrideWithDifferentVisibilityCheck::ChangeKind::Any,40 "any"},41 {misc::OverrideWithDifferentVisibilityCheck::ChangeKind::Widening,42 "widening"},43 {misc::OverrideWithDifferentVisibilityCheck::ChangeKind::Narrowing,44 "narrowing"},45 };46 return {Mapping};47 }48};49 50namespace misc {51 52OverrideWithDifferentVisibilityCheck::OverrideWithDifferentVisibilityCheck(53 StringRef Name, ClangTidyContext *Context)54 : ClangTidyCheck(Name, Context),55 DetectVisibilityChange(56 Options.get("DisallowedVisibilityChange", ChangeKind::Any)),57 CheckDestructors(Options.get("CheckDestructors", false)),58 CheckOperators(Options.get("CheckOperators", false)),59 IgnoredFunctions(utils::options::parseStringList(60 Options.get("IgnoredFunctions", ""))) {}61 62void OverrideWithDifferentVisibilityCheck::storeOptions(63 ClangTidyOptions::OptionMap &Opts) {64 Options.store(Opts, "DisallowedVisibilityChange", DetectVisibilityChange);65 Options.store(Opts, "CheckDestructors", CheckDestructors);66 Options.store(Opts, "CheckOperators", CheckOperators);67 Options.store(Opts, "IgnoredFunctions",68 utils::options::serializeStringList(IgnoredFunctions));69}70 71void OverrideWithDifferentVisibilityCheck::registerMatchers(72 MatchFinder *Finder) {73 const auto IgnoredDecl =74 namedDecl(matchers::matchesAnyListedName(IgnoredFunctions));75 const auto FilterDestructors =76 CheckDestructors ? decl() : decl(unless(cxxDestructorDecl()));77 const auto FilterOperators =78 CheckOperators ? namedDecl() : namedDecl(unless(isOperatorDecl()));79 Finder->addMatcher(80 cxxMethodDecl(81 isVirtual(), FilterDestructors, FilterOperators,82 ofClass(83 cxxRecordDecl(unless(isExpansionInSystemHeader())).bind("class")),84 forEachOverridden(cxxMethodDecl(ofClass(cxxRecordDecl().bind("base")),85 unless(IgnoredDecl))86 .bind("base_func")))87 .bind("func"),88 this);89}90 91void OverrideWithDifferentVisibilityCheck::check(92 const MatchFinder::MatchResult &Result) {93 const auto *const MatchedFunction =94 Result.Nodes.getNodeAs<FunctionDecl>("func");95 if (!MatchedFunction->isCanonicalDecl())96 return;97 98 const auto *const ParentClass =99 Result.Nodes.getNodeAs<CXXRecordDecl>("class");100 const auto *const BaseClass = Result.Nodes.getNodeAs<CXXRecordDecl>("base");101 CXXBasePaths Paths;102 if (!ParentClass->isDerivedFrom(BaseClass, Paths))103 return;104 105 const auto *const OverriddenFunction =106 Result.Nodes.getNodeAs<FunctionDecl>("base_func");107 const AccessSpecifier ActualAccess = MatchedFunction->getAccess();108 AccessSpecifier OverriddenAccess = OverriddenFunction->getAccess();109 110 const CXXBaseSpecifier *InheritanceWithStrictVisibility = nullptr;111 for (const CXXBasePath &Path : Paths) {112 for (const CXXBasePathElement &Elem : Path) {113 if (Elem.Base->getAccessSpecifier() > OverriddenAccess) {114 OverriddenAccess = Elem.Base->getAccessSpecifier();115 InheritanceWithStrictVisibility = Elem.Base;116 }117 }118 }119 120 if (ActualAccess != OverriddenAccess) {121 if (DetectVisibilityChange == ChangeKind::Widening &&122 ActualAccess > OverriddenAccess)123 return;124 if (DetectVisibilityChange == ChangeKind::Narrowing &&125 ActualAccess < OverriddenAccess)126 return;127 128 if (InheritanceWithStrictVisibility) {129 diag(MatchedFunction->getLocation(),130 "visibility of function %0 is changed from %1 (through %1 "131 "inheritance of class %2) to %3")132 << MatchedFunction << OverriddenAccess133 << InheritanceWithStrictVisibility->getType() << ActualAccess;134 diag(InheritanceWithStrictVisibility->getBeginLoc(),135 "%0 is inherited as %1 here", DiagnosticIDs::Note)136 << InheritanceWithStrictVisibility->getType() << OverriddenAccess;137 } else {138 diag(MatchedFunction->getLocation(),139 "visibility of function %0 is changed from %1 in class %2 to %3")140 << MatchedFunction << OverriddenAccess << BaseClass << ActualAccess;141 }142 diag(OverriddenFunction->getLocation(), "function declared here as %0",143 DiagnosticIDs::Note)144 << OverriddenFunction->getAccess();145 }146}147 148} // namespace misc149 150} // namespace clang::tidy151