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 @@ -6,6 +6,7 @@ add_clang_library(clangTidyLLVMLibcModule CalleeNamespaceCheck.cpp ImplementationInNamespaceCheck.cpp + InlineFunctionDeclCheck.cpp LLVMLibcTidyModule.cpp RestrictSystemLibcHeadersCheck.cpp diff --git a/clang-tools-extra/clang-tidy/llvmlibc/InlineFunctionDeclCheck.h b/clang-tools-extra/clang-tidy/llvmlibc/InlineFunctionDeclCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/llvmlibc/InlineFunctionDeclCheck.h @@ -0,0 +1,44 @@ +//===-- InlineFunctionDeclCheck.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_INLINEFUNCTIONDECLCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVMLIBC_INLINEFUNCTIONDECLCHECK_H + +#include "../ClangTidyCheck.h" +#include "../utils/FileExtensionsUtils.h" + +namespace clang::tidy::llvm_libc { + +/// Checks that explicitly and implicitly inline functions in headers files +/// are tagged with the LIBC_INLINE macro. +/// +/// For more information about the LIBC_INLINE macro, see +/// https://libc.llvm.org/code_style.html. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/llvmlibc/inline-function-decl-check.html +class InlineFunctionDeclCheck : public ClangTidyCheck { +public: + InlineFunctionDeclCheck(StringRef Name, ClangTidyContext *Context); + + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } + + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + const StringRef RawStringHeaderFileExtensions; + utils::FileExtensionsSet HeaderFileExtensions; +}; + +} // namespace clang::tidy::llvm_libc + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVMLIBC_INLINEFUNCTIONDECLCHECK_H diff --git a/clang-tools-extra/clang-tidy/llvmlibc/InlineFunctionDeclCheck.cpp b/clang-tools-extra/clang-tidy/llvmlibc/InlineFunctionDeclCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/llvmlibc/InlineFunctionDeclCheck.cpp @@ -0,0 +1,71 @@ +//===-- InlineFunctionDeclCheck.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 "InlineFunctionDeclCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +#include "llvm/ADT/StringSet.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::llvm_libc { + +InlineFunctionDeclCheck::InlineFunctionDeclCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + RawStringHeaderFileExtensions(Options.getLocalOrGlobal( + "HeaderFileExtensions", utils::defaultHeaderFileExtensions())) { + if (!utils::parseFileExtensions(RawStringHeaderFileExtensions, + HeaderFileExtensions, + utils::defaultFileExtensionDelimiters())) { + this->configurationDiag("Invalid header file extension: '%0'") + << RawStringHeaderFileExtensions; + } +} + +void InlineFunctionDeclCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(decl(functionDecl()).bind("func_decl"), this); +} + +void InlineFunctionDeclCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions); + if (!utils::parseFileExtensions(RawStringHeaderFileExtensions, + HeaderFileExtensions, + utils::defaultFileExtensionDelimiters())) { + this->configurationDiag("Invalid header file extension: '%0'") + << RawStringHeaderFileExtensions; + } +} + +void InlineFunctionDeclCheck::check(const MatchFinder::MatchResult &Result) { + const auto *FuncDecl = Result.Nodes.getNodeAs("func_decl"); + + // Consider only explicitly or implicitly inline functions. + if (FuncDecl == nullptr || !FuncDecl->isInlined()) + return; + + SourceLocation SrcBegin = FuncDecl->getBeginLoc(); + // Consider functions only in header files. + if (!utils::isSpellingLocInHeaderFile(SrcBegin, *Result.SourceManager, + HeaderFileExtensions)) + return; + + // Check if decl starts with LIBC_INLINE + auto Loc = FullSourceLoc(Result.SourceManager->getFileLoc(SrcBegin), + *Result.SourceManager); + llvm::StringRef SrcText = Loc.getBufferData().drop_front(Loc.getFileOffset()); + if (SrcText.starts_with("LIBC_INLINE")) + return; + + diag(SrcBegin, "%0 must be tagged with the LIBC_INLINE macro; the macro " + "should be placed at the beginning of the declaration") + << FuncDecl; +} + +} // namespace clang::tidy::llvm_libc 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 @@ -11,6 +11,7 @@ #include "../ClangTidyModuleRegistry.h" #include "CalleeNamespaceCheck.h" #include "ImplementationInNamespaceCheck.h" +#include "InlineFunctionDeclCheck.h" #include "RestrictSystemLibcHeadersCheck.h" namespace clang::tidy { @@ -23,6 +24,8 @@ "llvmlibc-callee-namespace"); CheckFactories.registerCheck( "llvmlibc-implementation-in-namespace"); + CheckFactories.registerCheck( + "llvmlibc-inline-function-decl"); 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 @@ -110,6 +110,12 @@ Warns when lambda specify a capture default and capture ``this``. +- New :doc: `llvmlibc-inline-function-decl + ` check. + + Checks that all implicit and explicit inline functions in header files are + tagged with the ``LIBC_INLINE`` macro. + New check aliases ^^^^^^^^^^^^^^^^^ 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 @@ -244,6 +244,7 @@ `llvm-twine-local `_, "Yes" `llvmlibc-callee-namespace `_, `llvmlibc-implementation-in-namespace `_, + `llvmlibc-inline-function-decl `_, `llvmlibc-restrict-system-libc-headers `_, "Yes" `misc-confusable-identifiers `_, `misc-const-correctness `_, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/llvmlibc/inline-function-decl.rst b/clang-tools-extra/docs/clang-tidy/checks/llvmlibc/inline-function-decl.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/llvmlibc/inline-function-decl.rst @@ -0,0 +1,19 @@ +.. title:: clang-tidy - llvmlibc-inline-function-decl + +llvmlibc-inline-function-decl +============================= + +Checks that all implicit and explicit inline functions in header files are +tagged with the ``LIBC_INLINE`` macro. See the `libc style guide +`_ for more information about this macro. + +Options +------- + +.. option:: HeaderFileExtensions + + A comma-separated list of filename extensions of header files (the filename + extensions should not include "." prefix). Default is "h,hh,hpp,hxx". + 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). diff --git a/clang-tools-extra/test/clang-tidy/checkers/llvmlibc/inline-function-decl.hpp b/clang-tools-extra/test/clang-tidy/checkers/llvmlibc/inline-function-decl.hpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/llvmlibc/inline-function-decl.hpp @@ -0,0 +1,65 @@ +// RUN: %check_clang_tidy %s llvmlibc-inline-function-decl %t + +#ifndef LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_LLVMLIBC_INLINEFUNCTIONDECL_H +#define LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_LLVMLIBC_INLINEFUNCTIONDECL_H + +#define LIBC_INLINE inline + +namespace __llvm_libc { + +LIBC_INLINE int addi(int a, int b) { + return a + b; +} + +LIBC_INLINE constexpr long addl(long a, long b) { + return a + b; +} + +constexpr long long addll(long long a, long long b) { +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: 'addll' must be tagged with the LIBC_INLINE macro; the macro should be placed at the beginning of the declaration [llvmlibc-inline-function-decl] + return a + b; +} + +inline unsigned long addul(unsigned long a, unsigned long b) { +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: 'addul' must be tagged with the LIBC_INLINE macro; the macro should be placed at the beginning of the declaration [llvmlibc-inline-function-decl] + return a + b; +} + +class MyClass { + int A; +public: + MyClass() : A(123) {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'MyClass' must be tagged with the LIBC_INLINE macro; the macro should be placed at the beginning of the declaration [llvmlibc-inline-function-decl] + + LIBC_INLINE MyClass(int V) : A(V) {} + + constexpr operator int() const { return A; } + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'operator int' must be tagged with the LIBC_INLINE macro; the macro should be placed at the beginning of the declaration [llvmlibc-inline-function-decl] + + LIBC_INLINE bool operator==(const MyClass &RHS) { + return RHS.A == A; + } + + static int getVal(const MyClass &V) { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'getVal' must be tagged with the LIBC_INLINE macro; the macro should be placed at the beginning of the declaration [llvmlibc-inline-function-decl] + return V.A; + } + + LIBC_INLINE static void setVal(MyClass &V, int A) { + V.A = A; + } + + constexpr static int addInt(MyClass &V, int A) { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'addInt' must be tagged with the LIBC_INLINE macro; the macro should be placed at the beginning of the declaration [llvmlibc-inline-function-decl] + return V.A += A; + } + + static LIBC_INLINE int mulInt(MyClass &V, int A) { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'mulInt' must be tagged with the LIBC_INLINE macro; the macro should be placed at the beginning of the declaration [llvmlibc-inline-function-decl] + return V.A *= A; + } +}; + +} // namespace __llvm_libc + +#endif // LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_LLVMLIBC_INLINEFUNCTIONDECL_H