From e0995a5a8df1a53128a94ff2db89a7a657273e05 Mon Sep 17 00:00:00 2001 From: Elliot Bobrow Date: Sat, 24 Jul 2021 10:58:55 -0700 Subject: [PATCH 01/10] fix code to suggest `;` on parse error --- .../rustc_parse/src/parser/diagnostics.rs | 114 +++++++++--------- .../issue-87197-missing-semicolon.fixed | 10 ++ .../parser/issue-87197-missing-semicolon.rs | 10 ++ .../issue-87197-missing-semicolon.stderr | 26 ++++ src/test/ui/parser/macros-no-semicolon.rs | 4 +- src/test/ui/parser/macros-no-semicolon.stderr | 18 ++- 6 files changed, 118 insertions(+), 64 deletions(-) create mode 100644 src/test/ui/parser/issue-87197-missing-semicolon.fixed create mode 100644 src/test/ui/parser/issue-87197-missing-semicolon.rs create mode 100644 src/test/ui/parser/issue-87197-missing-semicolon.stderr diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index b37caaebfb689..9818bd8d31469 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -242,6 +242,63 @@ impl<'a> Parser<'a> { expected.sort_by_cached_key(|x| x.to_string()); expected.dedup(); + let sm = self.sess.source_map(); + let msg = format!("expected `;`, found {}", super::token_descr(&self.token)); + let appl = Applicability::MachineApplicable; + if expected.contains(&TokenType::Token(token::Semi)) { + if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP { + // Likely inside a macro, can't provide meaningful suggestions. + } else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) { + // The current token is in the same line as the prior token, not recoverable. + } else if [token::Comma, token::Colon].contains(&self.token.kind) + && self.prev_token.kind == token::CloseDelim(token::Paren) + { + // Likely typo: The current token is on a new line and is expected to be + // `.`, `;`, `?`, or an operator after a close delimiter token. + // + // let a = std::process::Command::new("echo") + // .arg("1") + // ,arg("2") + // ^ + // https://github.com/rust-lang/rust/issues/72253 + } else if self.look_ahead(1, |t| { + t == &token::CloseDelim(token::Brace) + || t.can_begin_expr() && t.kind != token::Colon + }) && [token::Comma, token::Colon].contains(&self.token.kind) + { + // Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is + // either `,` or `:`, and the next token could either start a new statement or is a + // block close. For example: + // + // let x = 32: + // let y = 42; + self.bump(); + let sp = self.prev_token.span; + self.struct_span_err(sp, &msg) + .span_suggestion_short(sp, "change this to `;`", ";".to_string(), appl) + .emit(); + return Ok(false); + } else if self.look_ahead(0, |t| { + t == &token::CloseDelim(token::Brace) + || ( + t.can_begin_expr() && t != &token::Semi && t != &token::Pound + // Avoid triggering with too many trailing `#` in raw string. + ) + }) { + // Missing semicolon typo. This is triggered if the next token could either start a + // new statement or is a block close. For example: + // + // let x = 32 + // let y = 42; + let sp = self.prev_token.span.shrink_to_hi(); + self.struct_span_err(sp, &msg) + .span_label(self.token.span, "unexpected token") + .span_suggestion_short(sp, "add `;` here", ";".to_string(), appl) + .emit(); + return Ok(false); + } + } + let expect = tokens_to_string(&expected[..]); let actual = super::token_descr(&self.token); let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 { @@ -303,7 +360,6 @@ impl<'a> Parser<'a> { return Err(err); } - let sm = self.sess.source_map(); if self.prev_token.span == DUMMY_SP { // Account for macro context where the previous span might not be // available to avoid incorrect output (#54841). @@ -1144,62 +1200,6 @@ impl<'a> Parser<'a> { if self.eat(&token::Semi) { return Ok(()); } - let sm = self.sess.source_map(); - let msg = format!("expected `;`, found {}", super::token_descr(&self.token)); - let appl = Applicability::MachineApplicable; - if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP { - // Likely inside a macro, can't provide meaningful suggestions. - return self.expect(&token::Semi).map(drop); - } else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) { - // The current token is in the same line as the prior token, not recoverable. - } else if [token::Comma, token::Colon].contains(&self.token.kind) - && self.prev_token.kind == token::CloseDelim(token::Paren) - { - // Likely typo: The current token is on a new line and is expected to be - // `.`, `;`, `?`, or an operator after a close delimiter token. - // - // let a = std::process::Command::new("echo") - // .arg("1") - // ,arg("2") - // ^ - // https://github.com/rust-lang/rust/issues/72253 - self.expect(&token::Semi)?; - return Ok(()); - } else if self.look_ahead(1, |t| { - t == &token::CloseDelim(token::Brace) || t.can_begin_expr() && t.kind != token::Colon - }) && [token::Comma, token::Colon].contains(&self.token.kind) - { - // Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is - // either `,` or `:`, and the next token could either start a new statement or is a - // block close. For example: - // - // let x = 32: - // let y = 42; - self.bump(); - let sp = self.prev_token.span; - self.struct_span_err(sp, &msg) - .span_suggestion_short(sp, "change this to `;`", ";".to_string(), appl) - .emit(); - return Ok(()); - } else if self.look_ahead(0, |t| { - t == &token::CloseDelim(token::Brace) - || ( - t.can_begin_expr() && t != &token::Semi && t != &token::Pound - // Avoid triggering with too many trailing `#` in raw string. - ) - }) { - // Missing semicolon typo. This is triggered if the next token could either start a - // new statement or is a block close. For example: - // - // let x = 32 - // let y = 42; - let sp = self.prev_token.span.shrink_to_hi(); - self.struct_span_err(sp, &msg) - .span_label(self.token.span, "unexpected token") - .span_suggestion_short(sp, "add `;` here", ";".to_string(), appl) - .emit(); - return Ok(()); - } self.expect(&token::Semi).map(drop) // Error unconditionally } diff --git a/src/test/ui/parser/issue-87197-missing-semicolon.fixed b/src/test/ui/parser/issue-87197-missing-semicolon.fixed new file mode 100644 index 0000000000000..53f071db7819a --- /dev/null +++ b/src/test/ui/parser/issue-87197-missing-semicolon.fixed @@ -0,0 +1,10 @@ +// run-rustfix +// Parser should know when a semicolon is missing. +// https://github.com/rust-lang/rust/issues/87197 + +fn main() { + let x = 100; //~ ERROR: expected `;` + println!("{}", x); //~ ERROR: expected `;` + let y = 200; //~ ERROR: expected `;` + println!("{}", y); +} diff --git a/src/test/ui/parser/issue-87197-missing-semicolon.rs b/src/test/ui/parser/issue-87197-missing-semicolon.rs new file mode 100644 index 0000000000000..db0edf4529c77 --- /dev/null +++ b/src/test/ui/parser/issue-87197-missing-semicolon.rs @@ -0,0 +1,10 @@ +// run-rustfix +// Parser should know when a semicolon is missing. +// https://github.com/rust-lang/rust/issues/87197 + +fn main() { + let x = 100 //~ ERROR: expected `;` + println!("{}", x) //~ ERROR: expected `;` + let y = 200 //~ ERROR: expected `;` + println!("{}", y); +} diff --git a/src/test/ui/parser/issue-87197-missing-semicolon.stderr b/src/test/ui/parser/issue-87197-missing-semicolon.stderr new file mode 100644 index 0000000000000..57772de1e7a4b --- /dev/null +++ b/src/test/ui/parser/issue-87197-missing-semicolon.stderr @@ -0,0 +1,26 @@ +error: expected `;`, found `println` + --> $DIR/issue-87197-missing-semicolon.rs:6:16 + | +LL | let x = 100 + | ^ help: add `;` here +LL | println!("{}", x) + | ------- unexpected token + +error: expected `;`, found keyword `let` + --> $DIR/issue-87197-missing-semicolon.rs:7:22 + | +LL | println!("{}", x) + | ^ help: add `;` here +LL | let y = 200 + | --- unexpected token + +error: expected `;`, found `println` + --> $DIR/issue-87197-missing-semicolon.rs:8:16 + | +LL | let y = 200 + | ^ help: add `;` here +LL | println!("{}", y); + | ------- unexpected token + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/parser/macros-no-semicolon.rs b/src/test/ui/parser/macros-no-semicolon.rs index fd79fa8df7a68..24d1ae9e62368 100644 --- a/src/test/ui/parser/macros-no-semicolon.rs +++ b/src/test/ui/parser/macros-no-semicolon.rs @@ -1,5 +1,5 @@ fn main() { - assert_eq!(1, 2) - assert_eq!(3, 4) //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `assert_eq` + assert_eq!(1, 2) //~ ERROR: expected `;` + assert_eq!(3, 4) //~ ERROR: expected `;` println!("hello"); } diff --git a/src/test/ui/parser/macros-no-semicolon.stderr b/src/test/ui/parser/macros-no-semicolon.stderr index 9492191b8df14..f310662dbb0e7 100644 --- a/src/test/ui/parser/macros-no-semicolon.stderr +++ b/src/test/ui/parser/macros-no-semicolon.stderr @@ -1,10 +1,18 @@ -error: expected one of `.`, `;`, `?`, `}`, or an operator, found `assert_eq` - --> $DIR/macros-no-semicolon.rs:3:5 +error: expected `;`, found `assert_eq` + --> $DIR/macros-no-semicolon.rs:2:21 | LL | assert_eq!(1, 2) - | - expected one of `.`, `;`, `?`, `}`, or an operator + | ^ help: add `;` here LL | assert_eq!(3, 4) - | ^^^^^^^^^ unexpected token + | --------- unexpected token -error: aborting due to previous error +error: expected `;`, found `println` + --> $DIR/macros-no-semicolon.rs:3:21 + | +LL | assert_eq!(3, 4) + | ^ help: add `;` here +LL | println!("hello"); + | ------- unexpected token + +error: aborting due to 2 previous errors From f8eaa85b2b826f1d96a84e8aaf41db23973a0295 Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Sat, 24 Jul 2021 17:24:13 -0700 Subject: [PATCH 02/10] Flatten nested `format!` calls --- src/librustdoc/html/markdown.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 908e292d968ef..1fb5bb94b2c8a 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -235,9 +235,9 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { return Some(Event::Html( format!( "
\ - {}\ +
{}
\
", - format!(" class=\"language-{}\"", lang), + lang, Escape(&text), ) .into(), From f4861f3251c9f4ef4a0e2e8a2b6778be9b3d872c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 25 Jul 2021 11:31:57 +0200 Subject: [PATCH 03/10] Miri: santiy check that null pointer can never have an AllocId --- compiler/rustc_mir/src/interpret/memory.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir/src/interpret/memory.rs b/compiler/rustc_mir/src/interpret/memory.rs index 6dcd944a1c3f2..0396806f822fb 100644 --- a/compiler/rustc_mir/src/interpret/memory.rs +++ b/compiler/rustc_mir/src/interpret/memory.rs @@ -1142,7 +1142,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { Err(ptr) => ptr.into(), Ok(bits) => { let addr = u64::try_from(bits).unwrap(); - M::ptr_from_addr(&self, addr) + let ptr = M::ptr_from_addr(&self, addr); + if addr == 0 { + assert!(ptr.provenance.is_none(), "null pointer can never have an AllocId"); + } + ptr } } } From 76d1453b5b4df9cfb18d3b19bb2d35f476dfdb4e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 25 Jul 2021 17:38:44 +0100 Subject: [PATCH 04/10] freebsd remove compiler workaround. related issue #43575 --- compiler/rustc_target/src/spec/freebsd_base.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_target/src/spec/freebsd_base.rs b/compiler/rustc_target/src/spec/freebsd_base.rs index 998d6ffe0fc66..f2ec6aae9f2e6 100644 --- a/compiler/rustc_target/src/spec/freebsd_base.rs +++ b/compiler/rustc_target/src/spec/freebsd_base.rs @@ -1,4 +1,4 @@ -use crate::spec::{FramePointer, RelroLevel, TargetOptions}; +use crate::spec::{RelroLevel, TargetOptions}; pub fn opts() -> TargetOptions { TargetOptions { @@ -8,7 +8,6 @@ pub fn opts() -> TargetOptions { families: vec!["unix".to_string()], has_rpath: true, position_independent_executables: true, - frame_pointer: FramePointer::Always, // FIXME 43575: should be MayOmit... relro_level: RelroLevel::Full, abi_return_struct_as_int: true, dwarf_version: Some(2), From 70f282d46996ca381251890c28b9cd5226884ccd Mon Sep 17 00:00:00 2001 From: ibraheemdev Date: Sun, 25 Jul 2021 13:16:40 -0400 Subject: [PATCH 05/10] fix help message for modification to &T created by &{t} --- .../diagnostics/mutability_errors.rs | 16 +++++++-------- src/test/ui/borrowck/issue-85765.rs | 14 +++++++++++++ src/test/ui/borrowck/issue-85765.stderr | 20 ++++++++++++++++++- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs index 671d947d1b132..e23280beb61ba 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs @@ -119,9 +119,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { && !self.upvars.is_empty() { item_msg = format!("`{}`", access_place_desc.unwrap()); - debug_assert!( - self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_region_ptr() - ); + debug_assert!(self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL] + .ty + .is_region_ptr()); debug_assert!(is_closure_or_generator( Place::ty_from( the_place_err.local, @@ -905,6 +905,8 @@ fn suggest_ampmut<'tcx>( Some(c) if c.is_whitespace() => true, // e.g. `&mut(x)` Some('(') => true, + // e.g. `&mut{x}` + Some('{') => true, // e.g. `&mutablevar` _ => false, } @@ -912,9 +914,7 @@ fn suggest_ampmut<'tcx>( false } }; - if let (true, Some(ws_pos)) = - (src.starts_with("&'"), src.find(|c: char| -> bool { c.is_whitespace() })) - { + if let (true, Some(ws_pos)) = (src.starts_with("&'"), src.find(char::is_whitespace)) { let lt_name = &src[1..ws_pos]; let ty = src[ws_pos..].trim_start(); if !is_mutbl(ty) { @@ -940,9 +940,7 @@ fn suggest_ampmut<'tcx>( }; if let Ok(src) = tcx.sess.source_map().span_to_snippet(highlight_span) { - if let (true, Some(ws_pos)) = - (src.starts_with("&'"), src.find(|c: char| -> bool { c.is_whitespace() })) - { + if let (true, Some(ws_pos)) = (src.starts_with("&'"), src.find(char::is_whitespace)) { let lt_name = &src[1..ws_pos]; let ty = &src[ws_pos..]; return (highlight_span, format!("&{} mut{}", lt_name, ty)); diff --git a/src/test/ui/borrowck/issue-85765.rs b/src/test/ui/borrowck/issue-85765.rs index 1a5f7434fe2bb..2b1ab2f705057 100644 --- a/src/test/ui/borrowck/issue-85765.rs +++ b/src/test/ui/borrowck/issue-85765.rs @@ -12,4 +12,18 @@ fn main() { *r = 0; //~^ ERROR cannot assign to `*r`, which is behind a `&` reference //~| NOTE `r` is a `&` reference, so the data it refers to cannot be written + + #[rustfmt::skip] + let x: &usize = &mut{0}; + //~^ HELP consider changing this to be a mutable reference + *x = 1; + //~^ ERROR cannot assign to `*x`, which is behind a `&` reference + //~| NOTE `x` is a `&` reference, so the data it refers to cannot be written + + #[rustfmt::skip] + let y: &usize = &mut(0); + //~^ HELP consider changing this to be a mutable reference + *y = 1; + //~^ ERROR cannot assign to `*y`, which is behind a `&` reference + //~| NOTE `y` is a `&` reference, so the data it refers to cannot be written } diff --git a/src/test/ui/borrowck/issue-85765.stderr b/src/test/ui/borrowck/issue-85765.stderr index 4da4c8f594666..af83c6ea6d903 100644 --- a/src/test/ui/borrowck/issue-85765.stderr +++ b/src/test/ui/borrowck/issue-85765.stderr @@ -16,7 +16,25 @@ LL | LL | *r = 0; | ^^^^^^ `r` is a `&` reference, so the data it refers to cannot be written -error: aborting due to 2 previous errors +error[E0594]: cannot assign to `*x`, which is behind a `&` reference + --> $DIR/issue-85765.rs:19:5 + | +LL | let x: &usize = &mut{0}; + | - help: consider changing this to be a mutable reference: `&mut usize` +LL | +LL | *x = 1; + | ^^^^^^ `x` is a `&` reference, so the data it refers to cannot be written + +error[E0594]: cannot assign to `*y`, which is behind a `&` reference + --> $DIR/issue-85765.rs:26:5 + | +LL | let y: &usize = &mut(0); + | - help: consider changing this to be a mutable reference: `&mut usize` +LL | +LL | *y = 1; + | ^^^^^^ `y` is a `&` reference, so the data it refers to cannot be written + +error: aborting due to 4 previous errors Some errors have detailed explanations: E0594, E0596. For more information about an error, try `rustc --explain E0594`. From b4a873f54899c31525d3f1e1717b1d1a54191c21 Mon Sep 17 00:00:00 2001 From: ibraheemdev Date: Sun, 25 Jul 2021 13:35:06 -0400 Subject: [PATCH 06/10] fmt --- .../src/borrow_check/diagnostics/mutability_errors.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs index e23280beb61ba..336f48bde55ec 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs @@ -119,9 +119,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { && !self.upvars.is_empty() { item_msg = format!("`{}`", access_place_desc.unwrap()); - debug_assert!(self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL] - .ty - .is_region_ptr()); + debug_assert!( + self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_region_ptr() + ); debug_assert!(is_closure_or_generator( Place::ty_from( the_place_err.local, From dbb978a3c6d0503d188f11bbd4e53aac74d98bda Mon Sep 17 00:00:00 2001 From: Noah Lev Date: Sun, 25 Jul 2021 16:28:52 -0700 Subject: [PATCH 07/10] Remove unnecessary `structhead` parameter from `render_union` `structhead` is used for `render_struct` so that the logic for rendering structs can be shared between struct variants and struct items. However, `render_union` is not used anywhere except for rendering union items, so its `structhead` parameter is unnecessary. --- src/librustdoc/html/render/print_item.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 2dfbaff1cc995..9d6e34caa078b 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -888,7 +888,7 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni wrap_into_docblock(w, |w| { w.write_str("
");
         render_attributes_in_pre(w, it, "");
-        render_union(w, it, Some(&s.generics), &s.fields, "", true, cx);
+        render_union(w, it, Some(&s.generics), &s.fields, "", cx);
         w.write_str("
") }); @@ -1380,14 +1380,12 @@ fn render_union( g: Option<&clean::Generics>, fields: &[clean::Item], tab: &str, - structhead: bool, cx: &Context<'_>, ) { write!( w, - "{}{}{}", + "{}union {}", it.visibility.print_with_space(it.def_id, cx), - if structhead { "union " } else { "" }, it.name.as_ref().unwrap() ); if let Some(g) = g { From d1b032f52579a70dcf0385a97c1afbbe624a4d27 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Mon, 26 Jul 2021 10:31:28 +0200 Subject: [PATCH 08/10] Notify the Rust 2021 edition working group in zulip of edition bugs --- triagebot.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index bf30927448a80..187270fe7bcb6 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -112,6 +112,14 @@ message_on_add = """\ """ message_on_remove = "Issue #{number}'s nomination request has been removed." +[notify-zulip."edition 2021"] +required_labels = ["C-bug", "A-edition-2021"] +zulip_stream = 268952 # #edition 2021 +topic = "Edition Bugs" +message_on_add = """\ +Issue #{number} "{title}" has been added. +""" + [github-releases] format = "rustc" project-name = "Rust" From b0e67d5e3aa3d57705235dbd1722a26a4c1d43ba Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 26 Jul 2021 11:37:34 +0200 Subject: [PATCH 09/10] Add missing whitespace after attribute in HTML template --- src/librustdoc/html/templates/page.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html index 9b1bef5e44767..cc9c488db7c43 100644 --- a/src/librustdoc/html/templates/page.html +++ b/src/librustdoc/html/templates/page.html @@ -83,7 +83,7 @@ {#- -#} {%- endif -%} Date: Mon, 26 Jul 2021 11:38:46 +0200 Subject: [PATCH 10/10] Use correct syntax --- triagebot.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index 187270fe7bcb6..81c6719647fc1 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -112,8 +112,8 @@ message_on_add = """\ """ message_on_remove = "Issue #{number}'s nomination request has been removed." -[notify-zulip."edition 2021"] -required_labels = ["C-bug", "A-edition-2021"] +[notify-zulip."A-edition-2021"] +required_labels = ["C-bug"] zulip_stream = 268952 # #edition 2021 topic = "Edition Bugs" message_on_add = """\