[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[RFC v2 09/10] block: Add no_coroutine_fn marker
From: |
Alberto Faria |
Subject: |
[RFC v2 09/10] block: Add no_coroutine_fn marker |
Date: |
Fri, 29 Jul 2022 14:00:38 +0100 |
When applied to a function, it advertises that it should not be called
from coroutine_fn functions.
Make generated_co_wrapper evaluate to no_coroutine_fn, as coroutine_fn
functions should instead directly call the coroutine_fn that backs the
generated_co_wrapper.
Add a "no_coroutine_fn" check to static-analyzer.py that enforces
no_coroutine_fn rules.
Signed-off-by: Alberto Faria <afaria@redhat.com>
---
include/block/block-common.h | 2 +-
include/qemu/coroutine.h | 12 ++++
static_analyzer/no_coroutine_fn.py | 111 +++++++++++++++++++++++++++++
3 files changed, 124 insertions(+), 1 deletion(-)
create mode 100644 static_analyzer/no_coroutine_fn.py
diff --git a/include/block/block-common.h b/include/block/block-common.h
index fdb7306e78..4b60c8c6f2 100644
--- a/include/block/block-common.h
+++ b/include/block/block-common.h
@@ -42,7 +42,7 @@
*
* Read more in docs/devel/block-coroutine-wrapper.rst
*/
-#define generated_co_wrapper
+#define generated_co_wrapper no_coroutine_fn
/* block.c */
typedef struct BlockDriver BlockDriver;
diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h
index 26445b3176..c61dd2d3f7 100644
--- a/include/qemu/coroutine.h
+++ b/include/qemu/coroutine.h
@@ -48,6 +48,18 @@
#define coroutine_fn
#endif
+/**
+ * Mark a function that should never be called from a coroutine context
+ *
+ * This typically means that there is an analogous, coroutine_fn function that
+ * should be used instead.
+ */
+#ifdef __clang__
+#define no_coroutine_fn __attribute__((__annotate__("no_coroutine_fn")))
+#else
+#define no_coroutine_fn
+#endif
+
/**
* This can wrap a call to a coroutine_fn from a non-coroutine_fn function and
* suppress the static analyzer's complaints.
diff --git a/static_analyzer/no_coroutine_fn.py
b/static_analyzer/no_coroutine_fn.py
new file mode 100644
index 0000000000..1d0b93bb62
--- /dev/null
+++ b/static_analyzer/no_coroutine_fn.py
@@ -0,0 +1,111 @@
+# ----------------------------------------------------------------------------
#
+
+from clang.cindex import Cursor, CursorKind # type: ignore
+
+from static_analyzer import (
+ CheckContext,
+ VisitorResult,
+ check,
+ is_annotation,
+ is_annotated_with,
+ visit,
+)
+from static_analyzer.coroutine_fn import is_coroutine_fn
+
+# ----------------------------------------------------------------------------
#
+
+
+@check("no_coroutine_fn")
+def check_no_coroutine_fn(context: CheckContext) -> None:
+ """Reports violations of no_coroutine_fn rules."""
+
+ def visitor(node: Cursor) -> VisitorResult:
+
+ validate_annotations(context, node)
+
+ if node.kind == CursorKind.FUNCTION_DECL and node.is_definition():
+ check_calls(context, node)
+ return VisitorResult.CONTINUE
+
+ return VisitorResult.RECURSE
+
+ visit(context.translation_unit.cursor, visitor)
+
+
+def validate_annotations(context: CheckContext, node: Cursor) -> None:
+
+ # validate annotation usage
+
+ if is_annotation(node, "no_coroutine_fn") and (
+ node.parent is None or not is_valid_no_coroutine_fn_usage(node.parent)
+ ):
+ context.report(node, "invalid no_coroutine_fn usage")
+
+ # reject re-declarations with inconsistent annotations
+
+ if node.kind == CursorKind.FUNCTION_DECL and is_no_coroutine_fn(
+ node
+ ) != is_no_coroutine_fn(node.canonical):
+
+ context.report(
+ node,
+ f"no_coroutine_fn annotation disagreement with"
+ f" {context.format_location(node.canonical)}",
+ )
+
+
+def check_calls(context: CheckContext, caller: Cursor) -> None:
+ """
+ Reject calls from coroutine_fn to no_coroutine_fn.
+
+ Assumes that `caller` is a function definition.
+ """
+
+ if is_coroutine_fn(caller):
+
+ def visitor(node: Cursor) -> VisitorResult:
+
+ # We can get some "calls" that are actually things like top-level
+ # macro invocations for which `node.referenced` is None.
+
+ if (
+ node.kind == CursorKind.CALL_EXPR
+ and node.referenced is not None
+ and is_no_coroutine_fn(node.referenced.canonical)
+ ):
+ context.report(
+ node,
+ f"coroutine_fn calls no_coroutine_fn function"
+ f" {node.referenced.spelling}()",
+ )
+
+ return VisitorResult.RECURSE
+
+ visit(caller, visitor)
+
+
+# ----------------------------------------------------------------------------
#
+
+
+def is_valid_no_coroutine_fn_usage(parent: Cursor) -> bool:
+ """
+ Checks if an occurrence of `no_coroutine_fn` represented by a node with
+ parent `parent` appears at a valid point in the AST. This is the case if
the
+ parent is a function declaration/definition.
+ """
+
+ return parent.kind == CursorKind.FUNCTION_DECL
+
+
+def is_no_coroutine_fn(node: Cursor) -> bool:
+ """
+ Checks whether the given `node` should be considered to be
+ `no_coroutine_fn`.
+
+ This assumes valid usage of `no_coroutine_fn`.
+ """
+
+ return is_annotated_with(node, "no_coroutine_fn")
+
+
+# ----------------------------------------------------------------------------
#
--
2.37.1
- [RFC v2 00/10] Introduce an extensible static analyzer, Alberto Faria, 2022/07/29
- [RFC v2 01/10] Add an extensible static analyzer, Alberto Faria, 2022/07/29
- [RFC v2 02/10] Drop unused static function return values, Alberto Faria, 2022/07/29
- [RFC v2 03/10] static-analyzer: Support adding tests to checks, Alberto Faria, 2022/07/29
- [RFC v2 05/10] static-analyzer: Enforce coroutine_fn restrictions for direct calls, Alberto Faria, 2022/07/29
- [RFC v2 06/10] Fix some direct calls from non-coroutine_fn to coroutine_fn, Alberto Faria, 2022/07/29
- [RFC v2 04/10] static-analyzer: Avoid reanalyzing unmodified translation units, Alberto Faria, 2022/07/29
- [RFC v2 07/10] static-analyzer: Enforce coroutine_fn restrictions on function pointers, Alberto Faria, 2022/07/29
- [RFC v2 08/10] Fix some bad coroutine_fn indirect calls and pointer assignments, Alberto Faria, 2022/07/29
- [RFC v2 09/10] block: Add no_coroutine_fn marker,
Alberto Faria <=
- [RFC v2 10/10] Fix some calls from coroutine_fn to no_coroutine_fn, Alberto Faria, 2022/07/29