diff --git a/registry/channels/discord.json b/registry/channels/discord.json index 0b31a1e52d..8b9c0d4340 100644 --- a/registry/channels/discord.json +++ b/registry/channels/discord.json @@ -2,7 +2,7 @@ "name": "discord", "display_name": "Discord Channel", "kind": "channel", - "version": "0.2.2", + "version": "0.2.3", "wit_version": "0.3.0", "description": "Talk to your agent in Discord", "keywords": [ diff --git a/registry/channels/feishu.json b/registry/channels/feishu.json index 30bd1422f7..65bed7baf0 100644 --- a/registry/channels/feishu.json +++ b/registry/channels/feishu.json @@ -2,7 +2,7 @@ "name": "feishu", "display_name": "Feishu / Lark Channel", "kind": "channel", - "version": "0.1.4", + "version": "0.2.0", "wit_version": "0.3.0", "description": "Talk to your agent through a Feishu or Lark bot", "keywords": [ diff --git a/registry/channels/slack.json b/registry/channels/slack.json index 1f4db250a9..c08343938a 100644 --- a/registry/channels/slack.json +++ b/registry/channels/slack.json @@ -2,7 +2,7 @@ "name": "slack", "display_name": "Slack Channel", "kind": "channel", - "version": "0.2.2", + "version": "0.3.0", "wit_version": "0.3.0", "description": "Talk to your agent in Slack", "keywords": [ diff --git a/registry/channels/telegram.json b/registry/channels/telegram.json index 267d9c18c3..87291884ba 100644 --- a/registry/channels/telegram.json +++ b/registry/channels/telegram.json @@ -2,7 +2,7 @@ "name": "telegram", "display_name": "Telegram Channel", "kind": "channel", - "version": "0.2.6", + "version": "0.2.8", "wit_version": "0.3.0", "description": "Talk to your agent through a Telegram bot", "keywords": [ diff --git a/registry/channels/whatsapp.json b/registry/channels/whatsapp.json index be3faf0dc9..0e18151be5 100644 --- a/registry/channels/whatsapp.json +++ b/registry/channels/whatsapp.json @@ -2,7 +2,7 @@ "name": "whatsapp", "display_name": "WhatsApp Channel", "kind": "channel", - "version": "0.2.0", + "version": "0.2.2", "wit_version": "0.3.0", "description": "Talk to your agent through WhatsApp", "keywords": [ diff --git a/scripts/check_no_panics.py b/scripts/check_no_panics.py index 1dc365ea0c..87a2676ad8 100644 --- a/scripts/check_no_panics.py +++ b/scripts/check_no_panics.py @@ -123,9 +123,25 @@ def sanitize_line(line: str, state: LexerState) -> str: i += 1 continue if ch == "'": - # This can misclassify lifetimes like `'a` as char literals. That only - # risks false negatives by masking later code on the same line. - state.in_char = True + # Distinguish char literals ('x', '\n') from lifetime annotations + # ('a, 'static). Lifetimes are an apostrophe followed by an ASCII + # letter or underscore and then a non-apostrophe (identifiers, not + # closing-quote). Misclassifying a lifetime as a char literal + # blanks the rest of the line, hiding braces and causing the + # brace-depth tracker to desync across the whole file. + if nxt and (nxt.isalpha() or nxt == "_"): + # Peek past the identifier to see if it's 'x' (char) or 'ident (lifetime). + j = i + 2 + while j < len(chars) and (chars[j].isalnum() or chars[j] == "_"): + j += 1 + if j < len(chars) and chars[j] == "'": + # Closing quote found -> char literal like 'a' or 'ab' (invalid but safe to skip). + state.in_char = True + else: + # No closing quote -> lifetime annotation; skip the apostrophe. + out[i] = " " + else: + state.in_char = True i += 1 continue @@ -347,6 +363,33 @@ class CheckNoPanicsTests(unittest.TestCase): self.assertFalse(contexts[4]) self.assertFalse(contexts[5]) + def test_lifetime_annotations_do_not_desync_braces(self) -> None: + """Lifetime annotations ('a, 'static) must not be parsed as char literals. + + If they are, the sanitizer blanks the rest of the line — including any + opening brace — and the brace-depth tracker desyncs. This caused + false positives in large test modules (e.g. server.rs). + """ + lines = [ + "#[cfg(test)]\n", + "mod tests {\n", + " fn set_env_var(key: &'static str) -> Guard {\n", + " let original = std::env::var(key).ok();\n", + " Guard { key, original }\n", + " }\n", + " fn later_helper() {\n", + ' value.expect("should be test context");\n', + " }\n", + "}\n", + ] + + contexts = line_test_contexts(lines) + + # All lines inside mod tests must be test context, even after + # a function signature containing a lifetime annotation. + self.assertTrue(contexts[2], "fn with 'static should be test context") + self.assertTrue(contexts[7], "later helper should still be test context") + def test_named_tests_module_marks_context(self) -> None: lines = [ "mod tests {\n",