[clang-tidy] Added check 'misc-override-with-different-visibility' (#140086)
This commit is contained in:
parent
87e6fd161a
commit
a0f325bd41
@ -32,6 +32,7 @@ add_clang_library(clangTidyMiscModule STATIC
|
|||||||
NoRecursionCheck.cpp
|
NoRecursionCheck.cpp
|
||||||
NonCopyableObjects.cpp
|
NonCopyableObjects.cpp
|
||||||
NonPrivateMemberVariablesInClassesCheck.cpp
|
NonPrivateMemberVariablesInClassesCheck.cpp
|
||||||
|
OverrideWithDifferentVisibilityCheck.cpp
|
||||||
RedundantExpressionCheck.cpp
|
RedundantExpressionCheck.cpp
|
||||||
StaticAssertCheck.cpp
|
StaticAssertCheck.cpp
|
||||||
ThrowByValueCatchByReferenceCheck.cpp
|
ThrowByValueCatchByReferenceCheck.cpp
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "NoRecursionCheck.h"
|
#include "NoRecursionCheck.h"
|
||||||
#include "NonCopyableObjects.h"
|
#include "NonCopyableObjects.h"
|
||||||
#include "NonPrivateMemberVariablesInClassesCheck.h"
|
#include "NonPrivateMemberVariablesInClassesCheck.h"
|
||||||
|
#include "OverrideWithDifferentVisibilityCheck.h"
|
||||||
#include "RedundantExpressionCheck.h"
|
#include "RedundantExpressionCheck.h"
|
||||||
#include "StaticAssertCheck.h"
|
#include "StaticAssertCheck.h"
|
||||||
#include "ThrowByValueCatchByReferenceCheck.h"
|
#include "ThrowByValueCatchByReferenceCheck.h"
|
||||||
@ -81,6 +82,8 @@ public:
|
|||||||
"misc-use-anonymous-namespace");
|
"misc-use-anonymous-namespace");
|
||||||
CheckFactories.registerCheck<UseInternalLinkageCheck>(
|
CheckFactories.registerCheck<UseInternalLinkageCheck>(
|
||||||
"misc-use-internal-linkage");
|
"misc-use-internal-linkage");
|
||||||
|
CheckFactories.registerCheck<OverrideWithDifferentVisibilityCheck>(
|
||||||
|
"misc-override-with-different-visibility");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1,150 @@
|
|||||||
|
//===--- OverrideWithDifferentVisibilityCheck.cpp - clang-tidy ------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "OverrideWithDifferentVisibilityCheck.h"
|
||||||
|
#include "../utils/Matchers.h"
|
||||||
|
#include "../utils/OptionsUtils.h"
|
||||||
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||||
|
|
||||||
|
using namespace clang::ast_matchers;
|
||||||
|
using namespace clang;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
AST_MATCHER(NamedDecl, isOperatorDecl) {
|
||||||
|
DeclarationName::NameKind const NK = Node.getDeclName().getNameKind();
|
||||||
|
return NK != DeclarationName::Identifier &&
|
||||||
|
NK != DeclarationName::CXXConstructorName &&
|
||||||
|
NK != DeclarationName::CXXDestructorName;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace clang::tidy {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct OptionEnumMapping<
|
||||||
|
misc::OverrideWithDifferentVisibilityCheck::ChangeKind> {
|
||||||
|
static llvm::ArrayRef<std::pair<
|
||||||
|
misc::OverrideWithDifferentVisibilityCheck::ChangeKind, StringRef>>
|
||||||
|
getEnumMapping() {
|
||||||
|
static constexpr std::pair<
|
||||||
|
misc::OverrideWithDifferentVisibilityCheck::ChangeKind, StringRef>
|
||||||
|
Mapping[] = {
|
||||||
|
{misc::OverrideWithDifferentVisibilityCheck::ChangeKind::Any,
|
||||||
|
"any"},
|
||||||
|
{misc::OverrideWithDifferentVisibilityCheck::ChangeKind::Widening,
|
||||||
|
"widening"},
|
||||||
|
{misc::OverrideWithDifferentVisibilityCheck::ChangeKind::Narrowing,
|
||||||
|
"narrowing"},
|
||||||
|
};
|
||||||
|
return {Mapping};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace misc {
|
||||||
|
|
||||||
|
OverrideWithDifferentVisibilityCheck::OverrideWithDifferentVisibilityCheck(
|
||||||
|
StringRef Name, ClangTidyContext *Context)
|
||||||
|
: ClangTidyCheck(Name, Context),
|
||||||
|
DetectVisibilityChange(
|
||||||
|
Options.get("DisallowedVisibilityChange", ChangeKind::Any)),
|
||||||
|
CheckDestructors(Options.get("CheckDestructors", false)),
|
||||||
|
CheckOperators(Options.get("CheckOperators", false)),
|
||||||
|
IgnoredFunctions(utils::options::parseStringList(
|
||||||
|
Options.get("IgnoredFunctions", ""))) {}
|
||||||
|
|
||||||
|
void OverrideWithDifferentVisibilityCheck::storeOptions(
|
||||||
|
ClangTidyOptions::OptionMap &Opts) {
|
||||||
|
Options.store(Opts, "DisallowedVisibilityChange", DetectVisibilityChange);
|
||||||
|
Options.store(Opts, "CheckDestructors", CheckDestructors);
|
||||||
|
Options.store(Opts, "CheckOperators", CheckOperators);
|
||||||
|
Options.store(Opts, "IgnoredFunctions",
|
||||||
|
utils::options::serializeStringList(IgnoredFunctions));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OverrideWithDifferentVisibilityCheck::registerMatchers(
|
||||||
|
MatchFinder *Finder) {
|
||||||
|
const auto IgnoredDecl =
|
||||||
|
namedDecl(matchers::matchesAnyListedName(IgnoredFunctions));
|
||||||
|
const auto FilterDestructors =
|
||||||
|
CheckDestructors ? decl() : decl(unless(cxxDestructorDecl()));
|
||||||
|
const auto FilterOperators =
|
||||||
|
CheckOperators ? namedDecl() : namedDecl(unless(isOperatorDecl()));
|
||||||
|
Finder->addMatcher(
|
||||||
|
cxxMethodDecl(
|
||||||
|
isVirtual(), FilterDestructors, FilterOperators,
|
||||||
|
ofClass(
|
||||||
|
cxxRecordDecl(unless(isExpansionInSystemHeader())).bind("class")),
|
||||||
|
forEachOverridden(cxxMethodDecl(ofClass(cxxRecordDecl().bind("base")),
|
||||||
|
unless(IgnoredDecl))
|
||||||
|
.bind("base_func")))
|
||||||
|
.bind("func"),
|
||||||
|
this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OverrideWithDifferentVisibilityCheck::check(
|
||||||
|
const MatchFinder::MatchResult &Result) {
|
||||||
|
const auto *const MatchedFunction =
|
||||||
|
Result.Nodes.getNodeAs<FunctionDecl>("func");
|
||||||
|
if (!MatchedFunction->isCanonicalDecl())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto *const ParentClass =
|
||||||
|
Result.Nodes.getNodeAs<CXXRecordDecl>("class");
|
||||||
|
const auto *const BaseClass = Result.Nodes.getNodeAs<CXXRecordDecl>("base");
|
||||||
|
CXXBasePaths Paths;
|
||||||
|
if (!ParentClass->isDerivedFrom(BaseClass, Paths))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto *const OverriddenFunction =
|
||||||
|
Result.Nodes.getNodeAs<FunctionDecl>("base_func");
|
||||||
|
AccessSpecifier const ActualAccess = MatchedFunction->getAccess();
|
||||||
|
AccessSpecifier OverriddenAccess = OverriddenFunction->getAccess();
|
||||||
|
|
||||||
|
const CXXBaseSpecifier *InheritanceWithStrictVisibility = nullptr;
|
||||||
|
for (const CXXBasePath &Path : Paths) {
|
||||||
|
for (const CXXBasePathElement &Elem : Path) {
|
||||||
|
if (Elem.Base->getAccessSpecifier() > OverriddenAccess) {
|
||||||
|
OverriddenAccess = Elem.Base->getAccessSpecifier();
|
||||||
|
InheritanceWithStrictVisibility = Elem.Base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ActualAccess != OverriddenAccess) {
|
||||||
|
if (DetectVisibilityChange == ChangeKind::Widening &&
|
||||||
|
ActualAccess > OverriddenAccess)
|
||||||
|
return;
|
||||||
|
if (DetectVisibilityChange == ChangeKind::Narrowing &&
|
||||||
|
ActualAccess < OverriddenAccess)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (InheritanceWithStrictVisibility) {
|
||||||
|
diag(MatchedFunction->getLocation(),
|
||||||
|
"visibility of function %0 is changed from %1 (through %1 "
|
||||||
|
"inheritance of class %2) to %3")
|
||||||
|
<< MatchedFunction << OverriddenAccess
|
||||||
|
<< InheritanceWithStrictVisibility->getType() << ActualAccess;
|
||||||
|
diag(InheritanceWithStrictVisibility->getBeginLoc(),
|
||||||
|
"%0 is inherited as %1 here", DiagnosticIDs::Note)
|
||||||
|
<< InheritanceWithStrictVisibility->getType() << OverriddenAccess;
|
||||||
|
} else {
|
||||||
|
diag(MatchedFunction->getLocation(),
|
||||||
|
"visibility of function %0 is changed from %1 in class %2 to %3")
|
||||||
|
<< MatchedFunction << OverriddenAccess << BaseClass << ActualAccess;
|
||||||
|
}
|
||||||
|
diag(OverriddenFunction->getLocation(), "function declared here as %0",
|
||||||
|
DiagnosticIDs::Note)
|
||||||
|
<< OverriddenFunction->getAccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace misc
|
||||||
|
|
||||||
|
} // namespace clang::tidy
|
@ -0,0 +1,43 @@
|
|||||||
|
//===--- OverrideWithDifferentVisibilityCheck.h - clang-tidy --*- C++ -*---===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OVERRIDEWITHDIFFERENTVISIBILITYCHECK_H
|
||||||
|
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OVERRIDEWITHDIFFERENTVISIBILITYCHECK_H
|
||||||
|
|
||||||
|
#include "../ClangTidyCheck.h"
|
||||||
|
|
||||||
|
namespace clang::tidy::misc {
|
||||||
|
|
||||||
|
/// Finds virtual function overrides with different visibility than the function
|
||||||
|
/// in the base class.
|
||||||
|
///
|
||||||
|
/// For the user-facing documentation see:
|
||||||
|
/// http://clang.llvm.org/extra/clang-tidy/checks/misc/override-with-different-visibility.html
|
||||||
|
class OverrideWithDifferentVisibilityCheck : public ClangTidyCheck {
|
||||||
|
public:
|
||||||
|
enum class ChangeKind { Any, Widening, Narrowing };
|
||||||
|
|
||||||
|
OverrideWithDifferentVisibilityCheck(StringRef Name,
|
||||||
|
ClangTidyContext *Context);
|
||||||
|
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
|
||||||
|
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||||
|
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||||
|
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
|
||||||
|
return LangOpts.CPlusPlus;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ChangeKind DetectVisibilityChange;
|
||||||
|
bool CheckDestructors;
|
||||||
|
bool CheckOperators;
|
||||||
|
std::vector<llvm::StringRef> IgnoredFunctions;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace clang::tidy::misc
|
||||||
|
|
||||||
|
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OVERRIDEWITHDIFFERENTVISIBILITYCHECK_H
|
@ -134,6 +134,12 @@ New checks
|
|||||||
Checks for uses of MLIR's old/to be deprecated ``OpBuilder::create<T>`` form
|
Checks for uses of MLIR's old/to be deprecated ``OpBuilder::create<T>`` form
|
||||||
and suggests using ``T::create`` instead.
|
and suggests using ``T::create`` instead.
|
||||||
|
|
||||||
|
- New :doc:`misc-override-with-different-visibility
|
||||||
|
<clang-tidy/checks/misc/override-with-different-visibility>` check.
|
||||||
|
|
||||||
|
Finds virtual function overrides with different visibility than the function
|
||||||
|
in the base class.
|
||||||
|
|
||||||
New check aliases
|
New check aliases
|
||||||
^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
@ -271,6 +271,7 @@ Clang-Tidy Checks
|
|||||||
:doc:`misc-no-recursion <misc/no-recursion>`,
|
:doc:`misc-no-recursion <misc/no-recursion>`,
|
||||||
:doc:`misc-non-copyable-objects <misc/non-copyable-objects>`,
|
:doc:`misc-non-copyable-objects <misc/non-copyable-objects>`,
|
||||||
:doc:`misc-non-private-member-variables-in-classes <misc/non-private-member-variables-in-classes>`,
|
:doc:`misc-non-private-member-variables-in-classes <misc/non-private-member-variables-in-classes>`,
|
||||||
|
:doc:`misc-override-with-different-visibility <misc/override-with-different-visibility>`,
|
||||||
:doc:`misc-redundant-expression <misc/redundant-expression>`, "Yes"
|
:doc:`misc-redundant-expression <misc/redundant-expression>`, "Yes"
|
||||||
:doc:`misc-static-assert <misc/static-assert>`, "Yes"
|
:doc:`misc-static-assert <misc/static-assert>`, "Yes"
|
||||||
:doc:`misc-throw-by-value-catch-by-reference <misc/throw-by-value-catch-by-reference>`,
|
:doc:`misc-throw-by-value-catch-by-reference <misc/throw-by-value-catch-by-reference>`,
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
.. title:: clang-tidy - misc-override-with-different-visibility
|
||||||
|
|
||||||
|
misc-override-with-different-visibility
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Finds virtual function overrides with different visibility than the function
|
||||||
|
in the base class. This includes for example if a virtual function declared as
|
||||||
|
``private`` is overridden and declared as ``public`` in a subclass. The detected
|
||||||
|
change is the modification of visibility resulting from keywords ``public``,
|
||||||
|
``protected``, ``private`` at overridden virtual functions. The check applies to
|
||||||
|
any normal virtual function and optionally to destructors or operators. Use of
|
||||||
|
the ``using`` keyword is not considered as visibility change by this check.
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: c++
|
||||||
|
|
||||||
|
class A {
|
||||||
|
public:
|
||||||
|
virtual void f_pub();
|
||||||
|
private:
|
||||||
|
virtual void f_priv();
|
||||||
|
};
|
||||||
|
|
||||||
|
class B: public A {
|
||||||
|
public:
|
||||||
|
void f_priv(); // warning: changed visibility from private to public
|
||||||
|
private:
|
||||||
|
void f_pub(); // warning: changed visibility from public to private
|
||||||
|
};
|
||||||
|
|
||||||
|
class C: private A {
|
||||||
|
// no warning: f_pub becomes private in this case but this is from the
|
||||||
|
// private inheritance
|
||||||
|
};
|
||||||
|
|
||||||
|
class D: private A {
|
||||||
|
public:
|
||||||
|
void f_pub(); // warning: changed visibility from private to public
|
||||||
|
// 'f_pub' would have private access but is forced to be
|
||||||
|
// public
|
||||||
|
};
|
||||||
|
|
||||||
|
If the visibility is changed in this way, it can indicate bad design or
|
||||||
|
programming error.
|
||||||
|
|
||||||
|
If a virtual function is private in a subclass but public in the base class, it
|
||||||
|
can still be accessed from a pointer to the subclass if the pointer is converted
|
||||||
|
to the base type. Probably private inheritance can be used instead.
|
||||||
|
|
||||||
|
A protected virtual function that is made public in a subclass may have valid
|
||||||
|
use cases but similar (not exactly same) effect can be achieved with the
|
||||||
|
``using`` keyword.
|
||||||
|
|
||||||
|
Options
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. option:: DisallowedVisibilityChange
|
||||||
|
|
||||||
|
Controls what kind of change to the visibility will be detected by the check.
|
||||||
|
Possible values are `any`, `widening`, `narrowing`. For example the
|
||||||
|
`widening` option will produce warning only if the visibility is changed
|
||||||
|
from more restrictive (``private``) to less restrictive (``public``).
|
||||||
|
Default value is `any`.
|
||||||
|
|
||||||
|
.. option:: CheckDestructors
|
||||||
|
|
||||||
|
If `true`, the check does apply to destructors too. Otherwise destructors
|
||||||
|
are ignored by the check.
|
||||||
|
Default value is `false`.
|
||||||
|
|
||||||
|
.. option:: CheckOperators
|
||||||
|
|
||||||
|
If `true`, the check does apply to overloaded C++ operators (as virtual
|
||||||
|
member functions) too. This includes other special member functions (like
|
||||||
|
conversions) too. This option is probably useful only in rare cases because
|
||||||
|
operators and conversions are not often virtual functions.
|
||||||
|
Default value is `false`.
|
||||||
|
|
||||||
|
.. option:: IgnoredFunctions
|
||||||
|
|
||||||
|
This option can be used to ignore the check at specific functions.
|
||||||
|
To configure this option, a semicolon-separated list of function names
|
||||||
|
should be provided. The list can contain regular expressions, in this way it
|
||||||
|
is possible to select all functions of a specific class (like `MyClass::.*`)
|
||||||
|
or a specific function of any class (like `my_function` or
|
||||||
|
`::.*::my_function`). The function names are matched at the base class.
|
||||||
|
Default value is empty string.
|
@ -0,0 +1,14 @@
|
|||||||
|
#pragma clang system_header
|
||||||
|
|
||||||
|
namespace sys {
|
||||||
|
|
||||||
|
struct Base {
|
||||||
|
virtual void publicF();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Derived: public Base {
|
||||||
|
private:
|
||||||
|
void publicF() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
// RUN: %check_clang_tidy %s misc-override-with-different-visibility %t -- \
|
||||||
|
// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.IgnoredFunctions: 'IgnoreAlways::.*;::a::IgnoreSelected::.*;IgnoreFunctions::f1;ignored_f'}}"
|
||||||
|
|
||||||
|
class IgnoreAlways {
|
||||||
|
virtual void f();
|
||||||
|
};
|
||||||
|
|
||||||
|
class IgnoreSelected {
|
||||||
|
virtual void f();
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace a {
|
||||||
|
class IgnoreAlways {
|
||||||
|
virtual void f();
|
||||||
|
};
|
||||||
|
class IgnoreSelected {
|
||||||
|
virtual void f();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ignore_always {
|
||||||
|
class Test1: public IgnoreAlways {
|
||||||
|
public:
|
||||||
|
void f();
|
||||||
|
void ignored_f(int);
|
||||||
|
};
|
||||||
|
class Test2: public a::IgnoreAlways {
|
||||||
|
public:
|
||||||
|
void f();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ignore_selected {
|
||||||
|
class Test1: public IgnoreSelected {
|
||||||
|
public:
|
||||||
|
void f();
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'f'
|
||||||
|
// CHECK-MESSAGES: :9:16: note: function declared here
|
||||||
|
void ignored_f(int);
|
||||||
|
};
|
||||||
|
class Test2: public a::IgnoreSelected {
|
||||||
|
public:
|
||||||
|
void f();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class IgnoreFunctions {
|
||||||
|
virtual void f1();
|
||||||
|
virtual void f2();
|
||||||
|
virtual void ignored_f();
|
||||||
|
};
|
||||||
|
|
||||||
|
class IgnoreFunctionsTest: public IgnoreFunctions {
|
||||||
|
public:
|
||||||
|
void f1();
|
||||||
|
void f2();
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'f2'
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-9]]:16: note: function declared here
|
||||||
|
void ignored_f();
|
||||||
|
};
|
@ -0,0 +1,75 @@
|
|||||||
|
// RUN: %check_clang_tidy -check-suffixes=DTORS,WIDENING,NARROWING %s misc-override-with-different-visibility %t -- \
|
||||||
|
// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.CheckDestructors: true}}"
|
||||||
|
|
||||||
|
// RUN: %check_clang_tidy -check-suffixes=OPS,WIDENING,NARROWING %s misc-override-with-different-visibility %t -- \
|
||||||
|
// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.CheckOperators: true}}"
|
||||||
|
|
||||||
|
// RUN: %check_clang_tidy -check-suffixes=WIDENING %s misc-override-with-different-visibility %t -- \
|
||||||
|
// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.DisallowedVisibilityChange: 'widening'}}"
|
||||||
|
|
||||||
|
// RUN: %check_clang_tidy -check-suffixes=NARROWING %s misc-override-with-different-visibility %t -- \
|
||||||
|
// RUN: -config="{CheckOptions: {misc-override-with-different-visibility.DisallowedVisibilityChange: 'narrowing'}}"
|
||||||
|
|
||||||
|
namespace test_change {
|
||||||
|
|
||||||
|
class A {
|
||||||
|
protected:
|
||||||
|
virtual void f1();
|
||||||
|
virtual void f2();
|
||||||
|
};
|
||||||
|
|
||||||
|
class B: public A {
|
||||||
|
public:
|
||||||
|
void f1();
|
||||||
|
// CHECK-MESSAGES-WIDENING: :[[@LINE-1]]:8: warning: visibility of function 'f1'
|
||||||
|
// CHECK-MESSAGES-WIDENING: :[[@LINE-8]]:16: note: function declared here
|
||||||
|
private:
|
||||||
|
void f2();
|
||||||
|
// CHECK-MESSAGES-NARROWING: :[[@LINE-1]]:8: warning: visibility of function 'f2'
|
||||||
|
// CHECK-MESSAGES-NARROWING: :[[@LINE-11]]:16: note: function declared here
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_destructor {
|
||||||
|
|
||||||
|
class A {
|
||||||
|
public:
|
||||||
|
virtual ~A();
|
||||||
|
};
|
||||||
|
|
||||||
|
class B: public A {
|
||||||
|
protected:
|
||||||
|
~B();
|
||||||
|
// CHECK-MESSAGES-DTORS: :[[@LINE-1]]:3: warning: visibility of function '~B'
|
||||||
|
// CHECK-MESSAGES-DTORS: :[[@LINE-7]]:11: note: function declared here
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_operator {
|
||||||
|
|
||||||
|
class A {
|
||||||
|
virtual A& operator=(const A&);
|
||||||
|
virtual A& operator++();
|
||||||
|
virtual int operator()(int);
|
||||||
|
virtual operator double() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class B: public A {
|
||||||
|
protected:
|
||||||
|
A& operator=(const A&);
|
||||||
|
// CHECK-MESSAGES-OPS: :[[@LINE-1]]:6: warning: visibility of function 'operator='
|
||||||
|
// CHECK-MESSAGES-OPS: :[[@LINE-10]]:14: note: function declared here
|
||||||
|
A& operator++();
|
||||||
|
// CHECK-MESSAGES-OPS: :[[@LINE-1]]:6: warning: visibility of function 'operator++'
|
||||||
|
// CHECK-MESSAGES-OPS: :[[@LINE-12]]:14: note: function declared here
|
||||||
|
int operator()(int);
|
||||||
|
// CHECK-MESSAGES-OPS: :[[@LINE-1]]:7: warning: visibility of function 'operator()'
|
||||||
|
// CHECK-MESSAGES-OPS: :[[@LINE-14]]:15: note: function declared here
|
||||||
|
operator double() const;
|
||||||
|
// CHECK-MESSAGES-OPS: :[[@LINE-1]]:3: warning: visibility of function 'operator double'
|
||||||
|
// CHECK-MESSAGES-OPS: :[[@LINE-16]]:11: note: function declared here
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,289 @@
|
|||||||
|
// RUN: %check_clang_tidy %s misc-override-with-different-visibility %t -- -config="{CheckOptions: {misc-override-with-different-visibility.CheckDestructors: true,misc-override-with-different-visibility.CheckOperators: true}}" -- -I %S/Inputs/override-with-different-visibility
|
||||||
|
#include <test-system-header.h>
|
||||||
|
class A {
|
||||||
|
public:
|
||||||
|
virtual void pub_foo1() {}
|
||||||
|
virtual void pub_foo2() {}
|
||||||
|
virtual void pub_foo3() {}
|
||||||
|
protected:
|
||||||
|
virtual void prot_foo1();
|
||||||
|
virtual void prot_foo2();
|
||||||
|
virtual void prot_foo3();
|
||||||
|
private:
|
||||||
|
virtual void priv_foo1() {}
|
||||||
|
virtual void priv_foo2() {}
|
||||||
|
virtual void priv_foo3() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void A::prot_foo1() {}
|
||||||
|
void A::prot_foo2() {}
|
||||||
|
void A::prot_foo3() {}
|
||||||
|
|
||||||
|
namespace test1 {
|
||||||
|
|
||||||
|
class B: public A {
|
||||||
|
public:
|
||||||
|
void pub_foo1() override {}
|
||||||
|
void prot_foo1() override {}
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo1' is changed from protected in class 'A' to public [misc-override-with-different-visibility]
|
||||||
|
// CHECK-MESSAGES: :9:16: note: function declared here as protected
|
||||||
|
void priv_foo1() override {}
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo1' is changed from private in class 'A' to public [misc-override-with-different-visibility]
|
||||||
|
// CHECK-MESSAGES: :13:16: note: function declared here as private
|
||||||
|
protected:
|
||||||
|
void pub_foo2() override {}
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo2' is changed from public in class 'A' to protected [misc-override-with-different-visibility]
|
||||||
|
// CHECK-MESSAGES: :6:16: note: function declared here as public
|
||||||
|
void prot_foo2() override {}
|
||||||
|
void priv_foo2() override {}
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo2' is changed from private in class 'A' to protected [misc-override-with-different-visibility]
|
||||||
|
// CHECK-MESSAGES: :14:16: note: function declared here as private
|
||||||
|
private:
|
||||||
|
void pub_foo3() override {}
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo3' is changed from public in class 'A' to private [misc-override-with-different-visibility]
|
||||||
|
// CHECK-MESSAGES: :7:16: note: function declared here as public
|
||||||
|
void prot_foo3() override {}
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo3' is changed from protected in class 'A' to private [misc-override-with-different-visibility]
|
||||||
|
// CHECK-MESSAGES: :11:16: note: function declared here as protected
|
||||||
|
void priv_foo3() override {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class C: public B {
|
||||||
|
public:
|
||||||
|
void pub_foo1() override;
|
||||||
|
protected:
|
||||||
|
void prot_foo1() override;
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo1' is changed from public in class 'B' to protected [misc-override-with-different-visibility]
|
||||||
|
// CHECK-MESSAGES: :27:8: note: function declared here as public
|
||||||
|
private:
|
||||||
|
void priv_foo1() override;
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo1' is changed from public in class 'B' to private [misc-override-with-different-visibility]
|
||||||
|
// CHECK-MESSAGES: :30:8: note: function declared here as public
|
||||||
|
};
|
||||||
|
|
||||||
|
void C::prot_foo1() {}
|
||||||
|
void C::priv_foo1() {}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test2 {
|
||||||
|
|
||||||
|
class B: public A {
|
||||||
|
public:
|
||||||
|
void pub_foo1() override;
|
||||||
|
protected:
|
||||||
|
void prot_foo1() override;
|
||||||
|
private:
|
||||||
|
void priv_foo1() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class C: public B {
|
||||||
|
public:
|
||||||
|
void pub_foo1() override;
|
||||||
|
void prot_foo1() override;
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo1' is changed from protected in class 'B' to public
|
||||||
|
// CHECK-MESSAGES: :75:8: note: function declared here as protected
|
||||||
|
void priv_foo1() override;
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo1' is changed from private in class 'B' to public
|
||||||
|
// CHECK-MESSAGES: :77:8: note: function declared here as private
|
||||||
|
|
||||||
|
void pub_foo2() override;
|
||||||
|
void prot_foo2() override;
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo2' is changed from protected in class 'A' to public
|
||||||
|
// CHECK-MESSAGES: :10:16: note: function declared here as protected
|
||||||
|
void priv_foo2() override;
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo2' is changed from private in class 'A' to public
|
||||||
|
// CHECK-MESSAGES: :14:16: note: function declared here as private
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test3 {
|
||||||
|
|
||||||
|
class B: private A {
|
||||||
|
public:
|
||||||
|
void pub_foo1() override;
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo1' is changed from private (through private inheritance of class 'A') to public
|
||||||
|
// CHECK-MESSAGES: :103:10: note: 'A' is inherited as private here
|
||||||
|
// CHECK-MESSAGES: :5:16: note: function declared here as public
|
||||||
|
protected:
|
||||||
|
void prot_foo1() override;
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo1' is changed from private (through private inheritance of class 'A') to protected
|
||||||
|
// CHECK-MESSAGES: :103:10: note: 'A' is inherited as private here
|
||||||
|
// CHECK-MESSAGES: :9:16: note: function declared here as protected
|
||||||
|
private:
|
||||||
|
void priv_foo1() override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void prot_foo2() override;
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'prot_foo2' is changed from private (through private inheritance of class 'A') to public
|
||||||
|
// CHECK-MESSAGES: :103:10: note: 'A' is inherited as private here
|
||||||
|
// CHECK-MESSAGES: :10:16: note: function declared here as protected
|
||||||
|
void priv_foo2() override;
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'priv_foo2' is changed from private in class 'A' to public
|
||||||
|
// CHECK-MESSAGES: :14:16: note: function declared here as private
|
||||||
|
|
||||||
|
private:
|
||||||
|
void pub_foo3() override;
|
||||||
|
void prot_foo3() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class C: private A {
|
||||||
|
};
|
||||||
|
|
||||||
|
class D: public C {
|
||||||
|
public:
|
||||||
|
void pub_foo1() override;
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo1' is changed from private (through private inheritance of class 'A') to public
|
||||||
|
// CHECK-MESSAGES: :131:10: note: 'A' is inherited as private here
|
||||||
|
// CHECK-MESSAGES: :5:16: note: function declared here as public
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test4 {
|
||||||
|
|
||||||
|
struct Base1 {
|
||||||
|
public:
|
||||||
|
virtual void foo1();
|
||||||
|
private:
|
||||||
|
virtual void foo2();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Base2 {
|
||||||
|
public:
|
||||||
|
virtual void foo2();
|
||||||
|
private:
|
||||||
|
virtual void foo1();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct A : public Base1, public Base2 {
|
||||||
|
protected:
|
||||||
|
void foo1() override;
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'foo1' is changed from private in class 'Base2' to protected
|
||||||
|
// CHECK-MESSAGES: :158:16: note: function declared here as private
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-3]]:8: warning: visibility of function 'foo1' is changed from public in class 'Base1' to protected
|
||||||
|
// CHECK-MESSAGES: :149:16: note: function declared here as public
|
||||||
|
private:
|
||||||
|
void foo2() override;
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'foo2' is changed from public in class 'Base2' to private
|
||||||
|
// CHECK-MESSAGES: :156:16: note: function declared here as public
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test5 {
|
||||||
|
|
||||||
|
struct B1: virtual public A {};
|
||||||
|
struct B2: virtual private A {};
|
||||||
|
struct B: public B1, public B2 {
|
||||||
|
public:
|
||||||
|
void pub_foo1() override;
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'pub_foo1' is changed from private (through private inheritance of class 'A') to public
|
||||||
|
// CHECK-MESSAGES: :179:12: note: 'A' is inherited as private here
|
||||||
|
// CHECK-MESSAGES: :5:16: note: function declared here as public
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_using {
|
||||||
|
|
||||||
|
class A {
|
||||||
|
private:
|
||||||
|
A(int);
|
||||||
|
protected:
|
||||||
|
virtual void f();
|
||||||
|
};
|
||||||
|
|
||||||
|
class B: public A {
|
||||||
|
public:
|
||||||
|
using A::A;
|
||||||
|
using A::f;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_template {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class A {
|
||||||
|
protected:
|
||||||
|
virtual T foo();
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class B: public A<T> {
|
||||||
|
private:
|
||||||
|
T foo() override;
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: visibility of function 'foo' is changed from protected in class 'A<int>' to private
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-8]]:13: note: function declared here as protected
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class C: private A<T> {
|
||||||
|
public:
|
||||||
|
T foo() override;
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: visibility of function 'foo' is changed from private (through private inheritance of class 'A<int>') to public
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-4]]:10: note: 'A<int>' is inherited as private here
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-17]]:13: note: function declared here as protected
|
||||||
|
};
|
||||||
|
|
||||||
|
B<int> fB() {
|
||||||
|
return B<int>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
C<int> fC() {
|
||||||
|
return C<int>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_system_header {
|
||||||
|
|
||||||
|
struct SysDerived: public sys::Base {
|
||||||
|
private:
|
||||||
|
void publicF();
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: visibility of function 'publicF' is changed from public in class 'Base' to private
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_destructor {
|
||||||
|
|
||||||
|
class A {
|
||||||
|
public:
|
||||||
|
virtual ~A();
|
||||||
|
};
|
||||||
|
|
||||||
|
class B: public A {
|
||||||
|
protected:
|
||||||
|
~B();
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: visibility of function '~B'
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-7]]:11: note: function declared here
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace test_operator {
|
||||||
|
|
||||||
|
class A {
|
||||||
|
virtual int operator()(int);
|
||||||
|
virtual A& operator++();
|
||||||
|
virtual operator double() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class B: public A {
|
||||||
|
protected:
|
||||||
|
int operator()(int);
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: visibility of function 'operator()'
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-9]]:15: note: function declared here
|
||||||
|
A& operator++();
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: visibility of function 'operator++'
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-11]]:14: note: function declared here
|
||||||
|
operator double() const;
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: visibility of function 'operator double'
|
||||||
|
// CHECK-MESSAGES: :[[@LINE-13]]:11: note: function declared here
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user