From 918971cf6cce19528b9e5f54468f5a313394bfea Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Wed, 8 Jul 2026 16:27:01 -0700 Subject: [PATCH 1/4] `AssignCommand`: add regression tests for `@bot claim @user` So that when this command works to assign another user the difference is clear in tests. --- parser/src/command/assign.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/parser/src/command/assign.rs b/parser/src/command/assign.rs index d6807582c..3ebe6ce09 100644 --- a/parser/src/command/assign.rs +++ b/parser/src/command/assign.rs @@ -154,6 +154,11 @@ mod tests { ); } + #[test] + fn test_5() { + assert_eq!(parse("claim @user"), Ok(Some(AssignCommand::Claim)),); + } + fn parse_review<'a>(input: &'a str) -> Result, Error<'a>> { let mut toks = Tokenizer::new(input); Ok(AssignCommand::parse_review(&mut toks)?) From 89adf070c129deec8ee8c362bd2d759943cdaa80 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Wed, 8 Jul 2026 16:31:09 -0700 Subject: [PATCH 2/4] `AssignCommand`: add regression tests for `@bot claim` followed by non-user So that when we add support for `@bot claim @user` we don't accidentally introduce errors when `@bot claim` is followed by a non-username. --- parser/src/command/assign.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/parser/src/command/assign.rs b/parser/src/command/assign.rs index 3ebe6ce09..3a764a6ab 100644 --- a/parser/src/command/assign.rs +++ b/parser/src/command/assign.rs @@ -159,6 +159,14 @@ mod tests { assert_eq!(parse("claim @user"), Ok(Some(AssignCommand::Claim)),); } + #[test] + fn test_6() { + assert_eq!( + parse("claim does not start with @"), + Ok(Some(AssignCommand::Claim)), + ); + } + fn parse_review<'a>(input: &'a str) -> Result, Error<'a>> { let mut toks = Tokenizer::new(input); Ok(AssignCommand::parse_review(&mut toks)?) From 49deaadb88920fedcc077a6aa3175036b9754089 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Wed, 8 Jul 2026 16:44:58 -0700 Subject: [PATCH 3/4] Parser: add regression tests for `@bot claim` followed by another command So that when we add support for `@bot claim @user` we can see the behavior change. --- parser/src/command.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/parser/src/command.rs b/parser/src/command.rs index 30f46a119..783dbd8c3 100644 --- a/parser/src/command.rs +++ b/parser/src/command.rs @@ -519,3 +519,20 @@ fn delegate_with_at() { }))) ); } + +#[test] +fn test_follow_commands() { + let input = "@bot claim @bot label +bug"; + let mut input = Input::new(input, vec!["bot"]); + assert_eq!( + input.next(), + Some(Command::Assign(Ok(assign::AssignCommand::Claim))) + ); + assert_eq!( + input.next(), + Some(Command::Relabel(Ok(relabel::RelabelCommand(vec![ + relabel::LabelDelta::Add(relabel::Label("bug".into())) + ])))) + ); + assert_eq!(input.next(), None); +} From 277cbca215a786bddd3b3a59a0f03e28be712787 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Wed, 8 Jul 2026 16:58:28 -0700 Subject: [PATCH 4/4] `AssignCommand`: allow `@bot claim @user` to assign another user Fixes #2451 --- parser/src/command.rs | 4 +++- parser/src/command/assign.rs | 30 +++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/parser/src/command.rs b/parser/src/command.rs index 783dbd8c3..829f41332 100644 --- a/parser/src/command.rs +++ b/parser/src/command.rs @@ -526,7 +526,9 @@ fn test_follow_commands() { let mut input = Input::new(input, vec!["bot"]); assert_eq!( input.next(), - Some(Command::Assign(Ok(assign::AssignCommand::Claim))) + Some(Command::Assign(Ok(assign::AssignCommand::AssignUser { + username: "bot".to_string() + }))) ); assert_eq!( input.next(), diff --git a/parser/src/command/assign.rs b/parser/src/command/assign.rs index 3a764a6ab..2c88b89b8 100644 --- a/parser/src/command/assign.rs +++ b/parser/src/command/assign.rs @@ -5,7 +5,8 @@ //! The grammar is as follows: //! //! ```text -//! Command: `@bot claim`, `@bot release-assignment`, or `@bot assign @user`. +//! Command: `@bot claim`, `@bot release-assignment`, `@bot assign @user`, or +//! `@bot claim @user` //! ``` use crate::error::Error; @@ -18,7 +19,7 @@ pub enum AssignCommand { Claim, /// Corresponds to `@bot release-assignment` or `@bot unclaim`. ReleaseAssignment, - /// Corresponds to `@bot assign @user`. + /// Corresponds to `@bot assign @user` or `@bot claim @user`. AssignUser { username: String }, /// Corresponds to `r? [@]user`. RequestReview { name: String }, @@ -55,6 +56,14 @@ impl AssignCommand { let mut toks = input.clone(); if let Some(Token::Word("claim")) = toks.peek_token()? { toks.next_token()?; + if let Some(Token::Word(user)) = toks.peek_token()? + && user.starts_with('@') + && user.len() != 1 + { + return Ok(Some(AssignCommand::AssignUser { + username: user[1..].to_owned(), + })); + } if let Some(Token::Dot | Token::EndOfLine) = toks.peek_token()? { toks.next_token()?; } @@ -156,7 +165,12 @@ mod tests { #[test] fn test_5() { - assert_eq!(parse("claim @user"), Ok(Some(AssignCommand::Claim)),); + assert_eq!( + parse("claim @user"), + Ok(Some(AssignCommand::AssignUser { + username: "user".to_string() + })), + ); } #[test] @@ -167,6 +181,16 @@ mod tests { ); } + #[test] + fn test_7() { + assert_eq!( + parse("claim @triagebot"), + Ok(Some(AssignCommand::AssignUser { + username: "triagebot".to_string() + })), + ); + } + fn parse_review<'a>(input: &'a str) -> Result, Error<'a>> { let mut toks = Tokenizer::new(input); Ok(AssignCommand::parse_review(&mut toks)?)