-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[clang-tidy] Added check 'misc-override-with-different-visibility' #140086
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
65d44a4
[clang-tidy] Added check 'bugprone-function-visibility-change'
balazske f19ab2e
code and doc fixes, added option
balazske 8f1b20e
improvements, added options, check rename
balazske a63ba1e
fixed release notes
balazske db79972
improved documentation
balazske 4b279e8
small documentation change
balazske e020fe7
Merge branch 'main' into tidy_visibility_change_check
balazske 84cc917
move the check to 'misc'
balazske c2bb550
moved option checks to AST matcher
balazske 69233c1
corrected formatting error
balazske eeea1b3
Merge branch 'main' into tidy_visibility_change_check
balazske b58fabe
rearranged some code
balazske 434687b
Merge branch 'main' into tidy_visibility_change_check
balazske 181130f
rename
balazske 95eb94b
added
balazske 0da22cb
test and doc corrections
balazske File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
150 changes: 150 additions & 0 deletions
150
clang-tools-extra/clang-tidy/misc/OverrideWithDifferentVisibilityCheck.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
43 changes: 43 additions & 0 deletions
43
clang-tools-extra/clang-tidy/misc/OverrideWithDifferentVisibilityCheck.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
87 changes: 87 additions & 0 deletions
87
...-tools-extra/docs/clang-tidy/checks/misc/override-with-different-visibility.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. |
14 changes: 14 additions & 0 deletions
14
...t/clang-tidy/checkers/misc/Inputs/override-with-different-visibility/test-system-header.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| #pragma clang system_header | ||
|
|
||
| namespace sys { | ||
|
|
||
| struct Base { | ||
| virtual void publicF(); | ||
| }; | ||
|
|
||
| struct Derived: public Base { | ||
| private: | ||
| void publicF() override; | ||
| }; | ||
|
|
||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.