Index: clang-tidy/fuchsia/AddVisibilityCheck.h =================================================================== --- /dev/null +++ clang-tidy/fuchsia/AddVisibilityCheck.h @@ -0,0 +1,45 @@ +//===--- AddVisibilityCheck.h - clang-tidy-----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_FUCHSIA_ADDVISIBILITYCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_FUCHSIA_ADDVISIBILITYCHECK_H + +#include "../ClangTidy.h" +#include "../utils/HeaderFileExtensionsUtils.h" + +namespace clang { +namespace tidy { +namespace fuchsia { + +/// Finds functions given a list of names and suggests a fix to add +/// an `__attribute__((visibility("VISIBILITY")))` to their definitions, +/// if they do not already have a visibility attribute. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/fuchsia-add-visibility.html +class AddVisibilityCheck : public ClangTidyCheck { + public: + AddVisibilityCheck(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; + + private: + std::vector Names; + const std::string VisAttr; + Visibility Vis; + const std::string RawStringHeaderFileExtensions; + utils::HeaderFileExtensionsSet HeaderFileExtensions; +}; + +} // namespace fuchsia +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_FUCHSIA_ADDVISIBILITYCHECK_H Index: clang-tidy/fuchsia/AddVisibilityCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/fuchsia/AddVisibilityCheck.cpp @@ -0,0 +1,98 @@ +//===--- AddVisibilityCheck.cpp - clang-tidy-------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AddVisibilityCheck.h" +#include "../utils/OptionsUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Basic/Visibility.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace fuchsia { + +AST_MATCHER_P(FunctionDecl, hasVisibilityAttr, Visibility, V) { + return Node.getExplicitVisibility(NamedDecl::VisibilityForType) == V; +} + +// AST_MATCHER(FunctionDecl, isInHeaderFile) { +// return Node.getExplicitVisibility(NamedDecl::VisibilityForType); +// } + +AddVisibilityCheck::AddVisibilityCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + Names(utils::options::parseStringList( + Options.get("Names", "::std::vector"))), + VisAttr(Options.get("Visibility", "hidden")), + RawStringHeaderFileExtensions(Options.getLocalOrGlobal( + "HeaderFileExtensions", utils::defaultHeaderFileExtensions())) { + if (!utils::parseHeaderFileExtensions(RawStringHeaderFileExtensions, + HeaderFileExtensions, ',')) { + llvm::errs() << "Invalid header file extension: " + << RawStringHeaderFileExtensions << "\n"; + } + if (VisAttr == "hidden") + Vis = HiddenVisibility; + else if (VisAttr == "protected") + Vis = ProtectedVisibility; + else if (VisAttr == "default") + Vis = DefaultVisibility; + else + llvm::errs() << "Invalid visibliity attribute: " << VisAttr << "\n"; +} + +void AddVisibilityCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions); + Options.store(Opts, "Names", utils::options::serializeStringList(Names)); + Options.store(Opts, "Visibility", VisAttr); +} + +void AddVisibilityCheck::registerMatchers(MatchFinder *Finder) { + + Finder->addMatcher( + functionDecl(allOf(hasAnyName(SmallVector(Names.begin(), + Names.end())), + unless(hasVisibilityAttr(Vis)))) + .bind("no-visibility"), + this); +} + +void AddVisibilityCheck::check(const MatchFinder::MatchResult &Result) { + if (const auto *D = Result.Nodes.getNodeAs("no-visibility")) { + + // Ignore if it comes from the "main" file ... + if (Result.SourceManager->isInMainFile( + Result.SourceManager->getExpansionLoc(D->getLocStart()))) { + // unless that file is a header. + if (!utils::isSpellingLocInHeaderFile( + D->getLocStart(), *Result.SourceManager, HeaderFileExtensions)) + return; + } + + const Attr *A = D->getAttr(); + if (A && !A->isInherited() && A->getLocation().isValid()) + diag(D->getLocStart(), + "%0 visibility attribute not set for specified function.") + << VisAttr + << FixItHint::CreateReplacement(A->getRange(), + "visibility(\"" + VisAttr + "\")"); + else + diag(D->getLocStart(), + "no explicit visibility attribute set for specified function") + << FixItHint::CreateInsertion(D->getLocStart(), + "__attribute__((visibility(\"" + VisAttr + "\")))\n"); + } +} + +} // namespace fuchsia +} // namespace tidy +} // namespace clang Index: clang-tidy/fuchsia/CMakeLists.txt =================================================================== --- clang-tidy/fuchsia/CMakeLists.txt +++ clang-tidy/fuchsia/CMakeLists.txt @@ -1,6 +1,7 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyFuchsiaModule + AddVisibilityCheck.cpp DefaultArgumentsCheck.cpp FuchsiaTidyModule.cpp MultipleInheritanceCheck.cpp Index: clang-tidy/fuchsia/FuchsiaTidyModule.cpp =================================================================== --- clang-tidy/fuchsia/FuchsiaTidyModule.cpp +++ clang-tidy/fuchsia/FuchsiaTidyModule.cpp @@ -10,6 +10,7 @@ #include "../ClangTidy.h" #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" +#include "AddVisibilityCheck.h" #include "DefaultArgumentsCheck.h" #include "MultipleInheritanceCheck.h" #include "OverloadedOperatorCheck.h" @@ -27,6 +28,8 @@ class FuchsiaModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck( + "fuchsia-add-visibility"); CheckFactories.registerCheck( "fuchsia-default-arguments"); CheckFactories.registerCheck( Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -70,6 +70,13 @@ with looping constructs. Every backward jump is rejected. Forward jumps are only allowed in nested loops. +- New `fuchsia-add-visibility + `_ check + + Finds functions given a list of names and suggests a fix to add + an `__attribute__((visibility("VISIBILITY")))` to their definitions, + if they do not already have a visibility attribute. + - New `fuchsia-multiple-inheritance `_ check Index: docs/clang-tidy/checks/fuchsia-add-visibility.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/fuchsia-add-visibility.rst @@ -0,0 +1,45 @@ +.. title:: clang-tidy - fuchsia-add-visibility + +fuchsia-add-visibility +====================== + +Finds functions given a list of names and suggests a fix to add +an `__attribute__((visibility("VISIBILITY")))` to their header declaration, +if they do not already have a visibility attribute. + +Note: this will apply the fix to all declarations/definitions in header +files, so please use caution before applying it at large. + +For example, given the name 'func' in a header file: + +.. code-block:: c++ + + void func(); + +would be changed to + +.. code-block:: c++ + + __attribute__((visibility("hidden"))) + void func(); + +Options +------- + +.. option:: Names + + A string containing a comma-separated list of function names to check for + a visibility attribute. Default is an empty string. + +.. option:: Visibility + + A string specifying what visibility attribute to set, `hidden`, `default`, + or `protected`. Default is `hidden`. + +.. option:: HeaderFileExtensions + + A comma-separated list of filename extensions of header files (the filename + extensions should not contain "." prefix). Default is "h". + For header files without an extension, use an empty string (if there are no + other desired extensions) or leave an empty element in the list. e.g., + "h,hh,hpp,hxx," (note the trailing comma). Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -71,6 +71,7 @@ cppcoreguidelines-pro-type-vararg cppcoreguidelines-slicing cppcoreguidelines-special-member-functions + fuchsia-add-visibility fuchsia-default-arguments fuchsia-multiple-inheritance fuchsia-overloaded-operator Index: test/clang-tidy/fuchsia-add-visibility-pragma.hpp =================================================================== --- /dev/null +++ test/clang-tidy/fuchsia-add-visibility-pragma.hpp @@ -0,0 +1,36 @@ +// RUN: %check_clang_tidy %s fuchsia-add-visibility %t -- \ +// RUN: -config="{CheckOptions: [{key: fuchsia-add-visibility.Names, value: 'foo;bar;baz;foobar;f;g'}, \ +// RUN: {key: fuchsia-add-visibility.Visibility, value: 'protected'}]}" \ +// RUN: -header-filter=.* \ +// RUN: -- -std=c++11 + +#pragma GCC visibility push(hidden) + +void foo(); +// CHECK-MESSAGES: [[@LINE-1]]:1: warning: protected visibility attribute not set for specified function +// CHECK-FIXES: __attribute__((visibility("protected"))) + +__attribute__((visibility("protected"))) +void bar() {} + +__attribute__((visibility("protected"))) +void baz(); +void baz() {} + +__attribute__((visibility("default"))) +void foobar() {} +// CHECK-MESSAGES: [[@LINE-2]]:1: warning: protected visibility attribute not set for specified function +// CHECK-FIXES: __attribute__((visibility("protected"))) + +__attribute__((visibility("protected"))) +void foo(int); +void foo(double); +// CHECK-MESSAGES: [[@LINE-1]]:1: warning: protected visibility attribute not set for specified function +// CHECK-FIXES: __attribute__((visibility("protected"))) + +template +__attribute__((visibility("protected"))) +void foo(Ty); + +template <> +void foo(int); Index: test/clang-tidy/fuchsia-add-visibility.hpp =================================================================== --- /dev/null +++ test/clang-tidy/fuchsia-add-visibility.hpp @@ -0,0 +1,48 @@ +// RUN: %check_clang_tidy %s fuchsia-add-visibility %t -- \ +// RUN: -config="{CheckOptions: [{key: fuchsia-add-visibility.Names, value: 'foo;bar;baz;foobar;f;g;h;i'}, \ +// RUN: {key: fuchsia-add-visibility.Visibility, value: 'protected'}]}" \ +// RUN: -header-filter=.* \ +// RUN: -- -std=c++11 + +void foo(); +// CHECK-MESSAGES: [[@LINE-1]]:1: warning: no explicit visibility attribute set for specified function +// CHECK-FIXES: __attribute__((visibility("protected"))) + +void foo(); +// CHECK-MESSAGES: [[@LINE-1]]:1: warning: no explicit visibility attribute set for specified function +// CHECK-FIXES: __attribute__((visibility("protected"))) + +__attribute__((visibility("default"))) +void i(); +// CHECK-MESSAGES: [[@LINE-2]]:1: warning: protected visibility attribute not set for specified function +// CHECK-FIXES: __attribute__((visibility("protected"))) + +__attribute__((visibility("default"))) +void i(); +// CHECK-MESSAGES: [[@LINE-2]]:1: warning: protected visibility attribute not set for specified function +// CHECK-FIXES: __attribute__((visibility("protected"))) + +__attribute__((visibility("protected"))) +void bar() {} + +__attribute__((visibility("protected"))) +void baz(); +void baz() {} + +__attribute__((visibility("default"))) +void foobar() {} +// CHECK-MESSAGES: [[@LINE-2]]:1: warning: protected visibility attribute not set for specified function +// CHECK-FIXES: __attribute__((visibility("protected"))) + +__attribute__((visibility("protected"))) +void foo(int); +void foo(double); +// CHECK-MESSAGES: [[@LINE-1]]:1: warning: no explicit visibility attribute set for specified function +// CHECK-FIXES: __attribute__((visibility("protected"))) + +template +__attribute__((visibility("protected"))) +void h(Ty); + +template <> +void h(int); Index: test/clang-tidy/fuchsia-add-visibility.cpp =================================================================== --- /dev/null +++ test/clang-tidy/fuchsia-add-visibility.cpp @@ -0,0 +1,10 @@ +// RUN: clang-tidy %s -checks=-*,fuchsia-add-visibility \ +// RUN: -config="{CheckOptions: [{key: fuchsia-add-visibility.Names, value: 'foo;bar;baz;foobar;f;g'}, \ +// RUN: {key: fuchsia-add-visibility.Visibility, value: 'protected'}]}" \ +// RUN: -- -std=c++11 | count 0 + +// Note: this test expects no diagnostics, but FileCheck cannot handle that, +// hence the use of | count 0. + +void foo(); +void foo() {}