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 @@ -4,6 +4,7 @@ ) add_clang_library(clangTidyLLVMLibcModule + CalleeNamespaceCheck.cpp ImplementationInNamespaceCheck.cpp LLVMLibcTidyModule.cpp RestrictSystemLibcHeadersCheck.cpp diff --git a/clang-tools-extra/clang-tidy/llvmlibc/CalleeNamespaceCheck.h b/clang-tools-extra/clang-tidy/llvmlibc/CalleeNamespaceCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/llvmlibc/CalleeNamespaceCheck.h @@ -0,0 +1,38 @@ +//===-- CalleeNamespaceCheck.h ----------------------------------*- 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_CALLEENAMESPACECHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVMLIBC_CALLEENAMESPACECHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace llvm_libc { + +/// Checks all calls resolve to functions within __llvm_libc namespace. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/llvmlibc-callee-namespace.html +class CalleeNamespaceCheck : public ClangTidyCheck { +public: + CalleeNamespaceCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace llvm_libc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVMLIBC_CALLEENAMESPACECHECK_H diff --git a/clang-tools-extra/clang-tidy/llvmlibc/CalleeNamespaceCheck.cpp b/clang-tools-extra/clang-tidy/llvmlibc/CalleeNamespaceCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/llvmlibc/CalleeNamespaceCheck.cpp @@ -0,0 +1,55 @@ +//===-- CalleeNamespaceCheck.cpp ------------------------------------------===// +// +// 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 "CalleeNamespaceCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace llvm_libc { + +// Gets the outermost namespace of a DeclContext, right under the Translation +// Unit. +const DeclContext *getOutermostNamespace(const DeclContext *Decl) { + if (Decl->getParent() && Decl->getParent()->isTranslationUnit()) + return Decl; + return getOutermostNamespace(Decl->getParent()); +} + +void CalleeNamespaceCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + declRefExpr(to(functionDecl().bind("func"))).bind("use-site"), this); +} + +void CalleeNamespaceCheck::check(const MatchFinder::MatchResult &Result) { + const auto *UsageSiteExpr = Result.Nodes.getNodeAs("use-site"); + const auto *FuncDecl = Result.Nodes.getNodeAs("func"); + + // Ignore compiler builtin functions. + if (FuncDecl->getBuiltinID() != 0) + return; + + // If the outermost namespace of the function is __llvm_libc, we're good. + const auto *NS = dyn_cast(getOutermostNamespace(FuncDecl)); + if (NS && NS->getName() == "__llvm_libc") + return; + + diag(UsageSiteExpr->getBeginLoc(), "%0 must resolve to a function declared " + "within the '__llvm_libc' namespace") + << FuncDecl; + + diag(FuncDecl->getLocation(), "currently resolves to", + clang::DiagnosticIDs::Note); +} + +} // 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 "CalleeNamespaceCheck.h" #include "ImplementationInNamespaceCheck.h" #include "RestrictSystemLibcHeadersCheck.h" @@ -19,6 +20,8 @@ class LLVMLibcModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck( + "llvmlibc-callee-namespace"); CheckFactories.registerCheck( "llvmlibc-implementation-in-namespace"); CheckFactories.registerCheck( 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 @@ -113,6 +113,11 @@ Flags use of the `C` standard library functions ``memset``, ``memcpy`` and ``memcmp`` and similar derivatives on non-trivial types. +- New :doc:`llvmlibc-callee-namespace + ` check. + + Checks all calls resolve to functions within ``__llvm_libc`` namespace. + - New :doc:`llvmlibc-implementation-in-namespace ` check. 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-callee-namespace `_, `llvmlibc-implementation-in-namespace `_, `llvmlibc-restrict-system-libc-headers `_, "Yes" `misc-definitions-in-headers `_, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/llvmlibc-callee-namespace.rst b/clang-tools-extra/docs/clang-tidy/checks/llvmlibc-callee-namespace.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/llvmlibc-callee-namespace.rst @@ -0,0 +1,24 @@ +.. title:: clang-tidy - llvmlibc-callee-namespace + +llvmlibc-callee-namespace +==================================== + +Checks all calls resolve to functions within ``__llvm_libc`` namespace. + +.. code-block:: c++ + + namespace __llvm_libc { + + // Allow calls with the fully qualified name. + __llvm_libc::strlen("hello"); + + // Allow calls to compiler provided functions. + (void)__builtin_abs(-1); + + // Bare calls are allowed as long as they resolve to the correct namespace. + strlen("world"); + + // Disallow calling into functions in the global namespace. + ::strlen("!"); + + } // namespace __llvm_libc diff --git a/clang-tools-extra/test/clang-tidy/checkers/llvmlibc-callee-namespace.cpp b/clang-tools-extra/test/clang-tidy/checkers/llvmlibc-callee-namespace.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/llvmlibc-callee-namespace.cpp @@ -0,0 +1,42 @@ +// RUN: %check_clang_tidy %s llvmlibc-callee-namespace %t + +namespace __llvm_libc { +namespace nested { +void nested_func() {} +} // namespace nested +void libc_api_func() {} +} // namespace __llvm_libc + +// Emulate a function from the public headers like string.h +void libc_api_func() {} + +namespace __llvm_libc { +void Test() { + // Allow calls with the fully qualified name. + __llvm_libc::libc_api_func(); + __llvm_libc::nested::nested_func(); + void (*qualifiedPtr)(void) = __llvm_libc::libc_api_func; + qualifiedPtr(); + + // Should not trigger on compiler provided function calls. + (void)__builtin_abs(-1); + + // Bare calls are allowed as long as they resolve to the correct namespace. + libc_api_func(); + nested::nested_func(); + void (*barePtr)(void) = __llvm_libc::libc_api_func; + barePtr(); + + // Disallow calling into global namespace for implemented entrypoints. + ::libc_api_func(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'libc_api_func' must resolve to a function declared within the '__llvm_libc' namespace + // CHECK-MESSAGES: :11:6: note: currently resolves to + + // Disallow indirect references to functions in global namespace. + void (*badPtr)(void) = ::libc_api_func; + badPtr(); + // CHECK-MESSAGES: :[[@LINE-2]]:26: warning: 'libc_api_func' must resolve to a function declared within the '__llvm_libc' namespace + // CHECK-MESSAGES: :11:6: note: currently resolves to +} + +} // namespace __llvm_libc