diff --git a/clang-tools-extra/clang-tidy/llvmlibc/CMakeLists.txt b/clang-tools-extra/clang-tidy/llvmlibc/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/llvmlibc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/llvmlibc/CMakeLists.txt @@ -1,6 +1,7 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyLLVMLibcModule + EntrypointNamespaceCheck.cpp LLVMLibcTidyModule.cpp RestrictSystemLibcHeadersCheck.cpp diff --git a/clang-tools-extra/clang-tidy/llvmlibc/EntrypointNamespaceCheck.h b/clang-tools-extra/clang-tidy/llvmlibc/EntrypointNamespaceCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/llvmlibc/EntrypointNamespaceCheck.h @@ -0,0 +1,40 @@ +//===--- EntrypointNamespaceCheck.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_LLVMLIBC_ENTRYPOINTNAMESPACECHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVMLIBC_ENTRYPOINTNAMESPACECHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace llvm_libc { + +/// Checks that llvmlibc implentations are within the correct namespace. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/llvmlibc-entrypoint-namespace.html +class EntrypointNamespaceCheck : public ClangTidyCheck { +public: + EntrypointNamespaceCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + ast_matchers::MatchFinder *Finder; +}; + +} // namespace llvm_libc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVMLIBC_ENTRYPOINTNAMESPACECHECK_H diff --git a/clang-tools-extra/clang-tidy/llvmlibc/EntrypointNamespaceCheck.cpp b/clang-tools-extra/clang-tidy/llvmlibc/EntrypointNamespaceCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/llvmlibc/EntrypointNamespaceCheck.cpp @@ -0,0 +1,93 @@ +//===--- EntrypointNamespaceCheck.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 "EntrypointNamespaceCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/MacroArgs.h" +#include "clang/Lex/PPCallbacks.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace llvm_libc { + +namespace { +class EntrypointPPCallbacks : public PPCallbacks { +public: + EntrypointPPCallbacks(EntrypointNamespaceCheck &Check, MatchFinder *Finder, + std::string EntrypointMacro) + : Check(Check), Finder(Finder), EntrypointMacro(EntrypointMacro) {} + + void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, + SourceRange Range, const MacroArgs *Args) override; + +private: + EntrypointNamespaceCheck &Check; + MatchFinder *Finder; + std::string EntrypointMacro; +}; + +void EntrypointPPCallbacks::MacroExpands(const Token &MacroNameTok, + const MacroDefinition &MD, + SourceRange Range, + const MacroArgs *Args) { + if (MacroNameTok.getIdentifierInfo()->getName() != EntrypointMacro) + return; + + // Get the name of the function passed to macro and register a matcher. + StringRef Name = Args->getUnexpArgument(0)->getIdentifierInfo()->getName(); + Finder->addMatcher(functionDecl(hasName(Name)).bind("libc_entry"), &Check); +} +} // End of anonymous namespace. + +void EntrypointNamespaceCheck::registerPPCallbacks( + const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { + std::string EntrypointMacro = + Options.get("EntrypointMacro", "LLVM_LIBC_ENTRYPOINT"); + PP->addPPCallbacks( + std::make_unique(*this, Finder, EntrypointMacro)); +} + +void EntrypointNamespaceCheck::registerMatchers(MatchFinder *Finder) { + this->Finder = Finder; +} + +void EntrypointNamespaceCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MatchedDecl = Result.Nodes.getNodeAs("libc_entry"); + + // Ignore declarations from header files. + if (Result.SourceManager->getFilename(MatchedDecl->getLocation()) + .endswith(".h")) + return; + + std::string RequiredNamespace = + Options.get("RequiredNamespace", "__llvm_libc"); + const DeclContext *EnclosingDecl = + MatchedDecl->getEnclosingNamespaceContext(); + // If there is no namespace, translation unit is returned. + if (!EnclosingDecl->isNamespace()) { + diag(MatchedDecl->getLocation(), + "function %0 is not defined in a namespace, please wrap implentation " + "in '%1' namespace.") + << MatchedDecl << RequiredNamespace; + return; + } + + const NamespaceDecl *Namespace = cast(EnclosingDecl); + if (Namespace->getName() != RequiredNamespace) { + diag(MatchedDecl->getLocation(), + "function %0 is defined in namespace %1, should be in '%2' namespace.") + << MatchedDecl << Namespace << RequiredNamespace; + } +} + +} // namespace llvm_libc +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/llvmlibc/LLVMLibcTidyModule.cpp b/clang-tools-extra/clang-tidy/llvmlibc/LLVMLibcTidyModule.cpp --- a/clang-tools-extra/clang-tidy/llvmlibc/LLVMLibcTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/llvmlibc/LLVMLibcTidyModule.cpp @@ -9,6 +9,7 @@ #include "../ClangTidy.h" #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" +#include "EntrypointNamespaceCheck.h" #include "RestrictSystemLibcHeadersCheck.h" namespace clang { @@ -18,6 +19,8 @@ class LLVMLibcModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck( + "llvmlibc-entrypoint-namespace"); CheckFactories.registerCheck( "llvmlibc-restrict-system-libc-headers"); } diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -92,7 +92,7 @@ Finds ``cnd_wait``, ``cnd_timedwait``, ``wait``, ``wait_for``, or ``wait_until`` function calls when the function is not invoked from a loop - that checks whether a condition predicate holds or the function has a + that checks whether a condition predicate holds or the function has a condition parameter. - New :doc:`bugprone-reserved-identifier @@ -113,6 +113,12 @@ Flags use of the `C` standard library functions ``memset``, ``memcpy`` and ``memcmp`` and similar derivatives on non-trivial types. +- New :doc:`llvmlibc-entrypoint-namespace + ` check. + + Finds where llvm-libc entrypoint macro is called and checks that it is wrapped + in the correct namespace. + - New :doc:`llvmlibc-restrict-system-libc-headers ` check. @@ -156,7 +162,7 @@ ^^^^^^^^^^^^^^^^^^^^^^^^^^ - Improved :doc:`readability-qualified-auto - ` check now supports a + ` check now supports a `AddConstToQualified` to enable adding ``const`` qualifiers to variables typed with ``auto *`` and ``auto &``. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -188,6 +188,7 @@ `llvm-prefer-isa-or-dyn-cast-in-conditionals `_, "Yes" `llvm-prefer-register-over-unsigned `_, "Yes" `llvm-twine-local `_, "Yes" + `llvmlibc-entrypoint-namespace `_, `llvmlibc-restrict-system-libc-headers `_, "Yes" `misc-definitions-in-headers `_, "Yes" `misc-misplaced-const `_, diff --git a/clang-tools-extra/docs/clang-tidy/checks/llvmlibc-entrypoint-namespace.rst b/clang-tools-extra/docs/clang-tidy/checks/llvmlibc-entrypoint-namespace.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/llvmlibc-entrypoint-namespace.rst @@ -0,0 +1,35 @@ +.. title:: clang-tidy - llvmlibc-entrypoint-namespace + +llvmlibc-entrypoint-namespace +============================= + +Finds where llvm-libc entrypoint macro is called and checks that it is wrapped +in the correct namespace. + +.. code-block:: c++ + + // Correct: entrypoint inside correct namespace. + namespace __llvm_libc { + void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {} + } + + // Incorrect: entrypoint not in a namespace. + void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {} + + // Incorrect: entrypoint inside incorrect namespace. + namespace something_else { + void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {} + } + +Options +------- + +.. option:: EntrypointMacro + + The name of the macro used when defining llvm-libc implementations. The + default is `LLVM_LIBC_ENTRYPOINT`. + +.. option:: RequiredNamespace + + The namespace that llvm-libc implementations must be wrapped in. The default + is `__llvm_libc`. diff --git a/clang-tools-extra/test/clang-tidy/checkers/llvmlibc-entrypoint-namespace.cpp b/clang-tools-extra/test/clang-tidy/checkers/llvmlibc-entrypoint-namespace.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/llvmlibc-entrypoint-namespace.cpp @@ -0,0 +1,24 @@ +// RUN: %check_clang_tidy %s llvmlibc-entrypoint-namespace %t + +#define LLVM_LIBC_ENTRYPOINT(name) name +#define SOMETHING_ELSE(name) name + +// use of entrypoint macro with correct namespace. +namespace __llvm_libc { +void LLVM_LIBC_ENTRYPOINT(correct_entrypoint)(char *param) {} +} // namespace __llvm_libc + +// different macros are ignored. +namespace something_else { +void SOMETHING_ELSE(different_macro)(char *param) {} +} // namespace something_else + +// Not in a namespace. +void LLVM_LIBC_ENTRYPOINT(missing_namespace)(char *param) {} +// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: function 'missing_namespace' is not defined in a namespace, please wrap implentation in '__llvm_libc' namespace. + +// Inside incorrect namespace. +namespace something_else { +void LLVM_LIBC_ENTRYPOINT(incorrect_namespace)(char *param) {} +} // namespace something_else +// CHECK-MESSAGES: :[[@LINE-2]]:27: warning: function 'incorrect_namespace' is defined in namespace 'something_else', should be in '__llvm_libc' namespace.