#include "MisplacedWideningCastCheck.h"
#include "MoveForwardingReferenceCheck.h"
#include "MultipleStatementMacroCheck.h"
+#include "NoEscapeCheck.h"
#include "NotNullTerminatedResultCheck.h"
#include "ParentVirtualCallCheck.h"
#include "PosixReturnCheck.h"
"bugprone-multiple-statement-macro");
CheckFactories.registerCheck<cppcoreguidelines::NarrowingConversionsCheck>(
"bugprone-narrowing-conversions");
+ CheckFactories.registerCheck<NoEscapeCheck>("bugprone-no-escape");
CheckFactories.registerCheck<NotNullTerminatedResultCheck>(
"bugprone-not-null-terminated-result");
CheckFactories.registerCheck<ParentVirtualCallCheck>(
MisplacedWideningCastCheck.cpp
MoveForwardingReferenceCheck.cpp
MultipleStatementMacroCheck.cpp
+ NoEscapeCheck.cpp
NotNullTerminatedResultCheck.cpp
ParentVirtualCallCheck.cpp
PosixReturnCheck.cpp
--- /dev/null
+//===--- NoEscapeCheck.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 "NoEscapeCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+void NoEscapeCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(callExpr(callee(functionDecl(hasName("::dispatch_async"))),
+ argumentCountIs(2),
+ hasArgument(1, blockExpr().bind("arg-block"))),
+ this);
+ Finder->addMatcher(callExpr(callee(functionDecl(hasName("::dispatch_after"))),
+ argumentCountIs(3),
+ hasArgument(2, blockExpr().bind("arg-block"))),
+ this);
+}
+
+void NoEscapeCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedEscapingBlock =
+ Result.Nodes.getNodeAs<BlockExpr>("arg-block");
+ const BlockDecl *EscapingBlockDecl = MatchedEscapingBlock->getBlockDecl();
+ for (const BlockDecl::Capture &CapturedVar : EscapingBlockDecl->captures()) {
+ const VarDecl *Var = CapturedVar.getVariable();
+ if (Var && Var->hasAttr<NoEscapeAttr>()) {
+ // FIXME: Add a method to get the location of the use of a CapturedVar so
+ // that we can diagnose the use of the pointer instead of the block.
+ diag(MatchedEscapingBlock->getBeginLoc(),
+ "pointer %0 with attribute 'noescape' is captured by an "
+ "asynchronously-executed block")
+ << Var;
+ diag(Var->getBeginLoc(), "the 'noescape' attribute is declared here.",
+ DiagnosticIDs::Note);
+ }
+ }
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- NoEscapeCheck.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_BUGPRONE_NOESCAPECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NOESCAPECHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Block arguments in `dispatch_async()` and `dispatch_after()` are guaranteed
+/// to escape. If those blocks capture any pointers with the `noescape`
+/// attribute, then we warn the user of their error.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-no-escape.html
+class NoEscapeCheck : public ClangTidyCheck {
+public:
+ NoEscapeCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.Blocks;
+ }
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NOESCAPECHECK_H
result of a memory allocation function (``malloc()``, ``calloc()``,
``realloc()``, ``alloca()``) instead of its argument.
+- New :doc:`bugprone-no-escape
+ <clang-tidy/checks/bugprone-no-escape>` check.
+
+ Finds pointers with the ``noescape`` attribute that are captured by an
+ asynchronously-executed block.
+
- New :doc:`bugprone-spuriously-wake-up-functions
<clang-tidy/checks/bugprone-spuriously-wake-up-functions>` check.
Now checks ``std::basic_string_view`` by default.
- Improved :doc:`readability-else-after-return
- <clang-tidy/checks/readability-else-after-return>` check now supports a
+ <clang-tidy/checks/readability-else-after-return>` check now supports a
`WarnOnConditionVariables` option to control whether to refactor condition
variables where possible.
- Improved :doc:`readability-identifier-naming
<clang-tidy/checks/readability-identifier-naming>` check.
- Now able to rename member references in class template definitions with
+ Now able to rename member references in class template definitions with
explicit access.
- Improved :doc:`readability-qualified-auto
--- /dev/null
+.. title:: clang-tidy - bugprone-no-escape
+
+bugprone-no-escape
+==================
+
+Finds pointers with the ``noescape`` attribute that are captured by an
+asynchronously-executed block. The block arguments in ``dispatch_async()`` and
+``dispatch_after()`` are guaranteed to escape, so it is an error if a pointer with the
+``noescape`` attribute is captured by one of these blocks.
+
+The following is an example of an invalid use of the ``noescape`` attribute.
+
+ .. code-block:: objc
+ void foo(__attribute__((noescape)) int *p) {
+ dispatch_async(queue, ^{
+ *p = 123;
+ });
+ });
`bugprone-misplaced-widening-cast <bugprone-misplaced-widening-cast.html>`_,
`bugprone-move-forwarding-reference <bugprone-move-forwarding-reference.html>`_, "Yes"
`bugprone-multiple-statement-macro <bugprone-multiple-statement-macro.html>`_,
+ `bugprone-no-escape <bugprone-no-escape.html>`_, "Yes"
`bugprone-not-null-terminated-result <bugprone-not-null-terminated-result.html>`_, "Yes"
`bugprone-parent-virtual-call <bugprone-parent-virtual-call.html>`_, "Yes"
`bugprone-posix-return <bugprone-posix-return.html>`_, "Yes"
--- /dev/null
+// RUN: %check_clang_tidy %s bugprone-no-escape %t
+// RUN: %check_clang_tidy %s -assume-filename=bugprone-no-escape.c bugprone-no-escape %t -- -- -fblocks
+
+typedef struct dispatch_queue_s *dispatch_queue_t;
+typedef struct dispatch_time_s *dispatch_time_t;
+typedef void (^dispatch_block_t)(void);
+void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
+void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
+
+extern dispatch_queue_t queue;
+
+void test_noescape_attribute(__attribute__((noescape)) int *p, int *q) {
+ dispatch_async(queue, ^{
+ *p = 123;
+ // CHECK-MESSAGES: :[[@LINE-2]]:25: warning: pointer 'p' with attribute 'noescape' is captured by an asynchronously-executed block [bugprone-no-escape]
+ // CHECK-MESSAGES: :[[@LINE-4]]:30: note: the 'noescape' attribute is declared here.
+ });
+
+ dispatch_after(456, queue, ^{
+ *p = 789;
+ // CHECK-MESSAGES: :[[@LINE-2]]:30: warning: pointer 'p' with attribute 'noescape' is captured by an asynchronously-executed block [bugprone-no-escape]
+ });
+
+ dispatch_async(queue, ^{
+ *q = 0;
+ // CHECK-MESSAGES-NOT: :[[@LINE-2]]:25: warning: pointer 'q' with attribute 'noescape' is captured by an asynchronously-executed block
+ });
+}