brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.5 KiB · 09c5269 Raw
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