Skip to content

Commit

Permalink
feat: improve malformed test attribute error (#6414)
Browse files Browse the repository at this point in the history
  • Loading branch information
asterite authored Oct 30, 2024
1 parent be882f1 commit 8f516d7
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 14 deletions.
8 changes: 8 additions & 0 deletions compiler/noirc_frontend/src/lexer/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub enum LexerErrorKind {
IntegerLiteralTooLarge { span: Span, limit: String },
#[error("{:?} is not a valid attribute", found)]
MalformedFuncAttribute { span: Span, found: String },
#[error("Malformed test attribute")]
MalformedTestAttribute { span: Span },
#[error("{:?} is not a valid inner attribute", found)]
InvalidInnerAttribute { span: Span, found: String },
#[error("Logical and used instead of bitwise and")]
Expand Down Expand Up @@ -61,6 +63,7 @@ impl LexerErrorKind {
LexerErrorKind::InvalidIntegerLiteral { span, .. } => *span,
LexerErrorKind::IntegerLiteralTooLarge { span, .. } => *span,
LexerErrorKind::MalformedFuncAttribute { span, .. } => *span,
LexerErrorKind::MalformedTestAttribute { span, .. } => *span,
LexerErrorKind::InvalidInnerAttribute { span, .. } => *span,
LexerErrorKind::LogicalAnd { span } => *span,
LexerErrorKind::UnterminatedBlockComment { span } => *span,
Expand Down Expand Up @@ -109,6 +112,11 @@ impl LexerErrorKind {
format!(" {found} is not a valid attribute"),
*span,
),
LexerErrorKind::MalformedTestAttribute { span } => (
"Malformed test attribute".to_string(),
"The test attribute can be written in one of these forms: `#[test]`, `#[test(should_fail)]` or `#[test(should_fail_with = \"message\")]`".to_string(),
*span,
),
LexerErrorKind::InvalidInnerAttribute { span, found } => (
"Invalid inner attribute".to_string(),
format!(" {found} is not a valid inner attribute"),
Expand Down
8 changes: 1 addition & 7 deletions compiler/noirc_frontend/src/lexer/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -935,13 +935,7 @@ mod tests {
Err(err) => err,
};

// Check if error is MalformedFuncAttribute and found is "foo"
let sub_string = match err {
LexerErrorKind::MalformedFuncAttribute { found, .. } => found,
_ => panic!("expected malformed func attribute error"),
};

assert_eq!(sub_string, "test(invalid_scope)");
assert!(matches!(err, LexerErrorKind::MalformedTestAttribute { .. }));
}

#[test]
Expand Down
20 changes: 13 additions & 7 deletions compiler/noirc_frontend/src/lexer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -751,10 +751,18 @@ impl Attribute {
contents_span: Span,
is_tag: bool,
) -> Result<Attribute, LexerErrorKind> {
let word_segments: Vec<&str> = word
.split(|c| c == '(' || c == ')')
.filter(|string_segment| !string_segment.is_empty())
.collect();
// See if we can parse the word into "name ( contents )".
// We first split into "first_segment ( rest".
let word_segments = if let Some((first_segment, rest)) = word.trim().split_once('(') {
// Now we try to remove the final ")" (it must be at the end, if it exists)
if let Some(middle) = rest.strip_suffix(')') {
vec![first_segment.trim(), middle.trim()]
} else {
vec![word]
}
} else {
vec![word]
};

let validate = |slice: &str| {
let is_valid = slice
Expand Down Expand Up @@ -799,11 +807,9 @@ impl Attribute {
["inline_always"] => Attribute::Function(FunctionAttribute::InlineAlways),
["test", name] => {
validate(name)?;
let malformed_scope =
LexerErrorKind::MalformedFuncAttribute { span, found: word.to_owned() };
match TestScope::lookup_str(name) {
Some(scope) => Attribute::Function(FunctionAttribute::Test(scope)),
None => return Err(malformed_scope),
None => return Err(LexerErrorKind::MalformedTestAttribute { span }),
}
}
["field", name] => {
Expand Down
9 changes: 9 additions & 0 deletions tooling/lsp/src/requests/completion/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ impl<'a> NodeFinder<'a> {
let one_argument_attributes = &["abi", "field", "foreign", "oracle"];
self.suggest_one_argument_attributes(prefix, one_argument_attributes);

if name_matches("test", prefix) || name_matches("should_fail", prefix) {
self.completion_items.push(snippet_completion_item(
"test(should_fail)",
CompletionItemKind::METHOD,
"test(should_fail)",
None,
));
}

if name_matches("test", prefix) || name_matches("should_fail_with", prefix) {
self.completion_items.push(snippet_completion_item(
"test(should_fail_with = \"...\")",
Expand Down

0 comments on commit 8f516d7

Please sign in to comment.